User Tools

Site Tools


Custom Controls in XAML

Create a custom control (a.k.a. templated control) in Visual Studio:

  • Add > New Item > Templated Control > Provide a name for your control, e.g. SimpleControl.
  • VS creates a class SimpleControl that inherits from Control.
  • VS creates a Themes folder with a Generic.xaml file in it.

Alternatively, you may want to create a UserControl first and then change it to a custom control by deriving it from a class different than the UserControl class, for example the Button class.

Example: A simple custom control:


    <Style TargetType="local:SimpleControl">
        <Setter Property="Template">
                <ControlTemplate TargetType="local:SimpleControl">
                    <Grid Height="52" BorderBrush="Blue" BorderThickness="1" Padding="8">
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition />
                        <TextBlock x:Name="SimpleLabel" 
                                   VerticalAlignment="Center" />
                        <TextBox x:Name="SimpleTextBox" 
                                 VerticalAlignment="Center" />

The SimpleControl class:

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace TestApp
    // The TemplatePart attribute allows you to specify the name and type of any parts of your 
    // templated control that you expect to be available for the control to function properly. 
    [TemplatePart(Name = "SimpleLabel", Type = typeof(TextBlock))]
    [TemplatePart(Name = "SimpleTextBox", Type = typeof(TextBox))]
    public sealed class SimpleControl : Control
        private TextBlock SimpleLabel;
        private TextBox SimpleTextBox;
        public string LabelText { get; set; }
        public string Text { get; set; }
        public SimpleControl()
            // Set the DefaultStyleKey property to the SimpleControl type.
            // This lets the framework know that this control supports templating and also 
            // identifies the key that will be used to find the style for this control.
            this.DefaultStyleKey = typeof(SimpleControl);
        protected override void OnApplyTemplate()
            // Retrieve the named elements in the instantiated ControlTemplate visual tree. 
            SimpleLabel = this.GetTemplateChild("SimpleLabel") as TextBlock;
            SimpleTextBox = this.GetTemplateChild("SimpleTextBox") as TextBox;
            // Check if the elements are present and set theis values.
            if (SimpleLabel != null && SimpleTextBox != null)
                SimpleLabel.Text = LabelText;
                SimpleTextBox.Text = Text;

Example: Another way of creating a custom control is to define a XAML file and a corresponding code-behind file. In the following example we create a custom button GradientButton:


<Button x:Class="TestApp.GradientButton"
        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
            <GradientStop Offset="0" Color="{x:Bind ColorBegin}" />
            <GradientStop Offset="1" Color="{x:Bind ColorEnd}" />


using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace TestApp
    public sealed partial class GradientButton : Button
        public static readonly DependencyProperty ColorBeginProperty =
            DependencyProperty.Register("ColorBegin", typeof(Color), typeof(GradientButton), 
                new PropertyMetadata(Colors.White));
        public static readonly DependencyProperty ColorEndProperty =
            DependencyProperty.Register("ColorEnd", typeof(Color), typeof(GradientButton), 
                new PropertyMetadata(Colors.Black));
        public Color ColorBegin
            get { return (Color)GetValue(ColorBeginProperty); }
            set { SetValue(ColorBeginProperty, value); }
        public Color ColorEnd
            get { return (Color)GetValue(ColorEndProperty); }
            set { SetValue(ColorEndProperty, value); }
        public GradientButton()

MainPage.xaml uses the GradientButton:

<Page x:Class="TestApp.MainPage"
        <local:GradientButton Foreground="White" Content="Test Button"
                              ColorBegin="Red" ColorEnd="Blue" />

Example: The PieSlice control is based on an example from the book “Programming Windows” by Charles Petzold:

using System;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
namespace TestApp
    public class PieSlice : Path
        // Store frequently used objects globally.
        private PathFigure pathFigure;
        private LineSegment lineSegment;
        private ArcSegment arcSegment;
        // Animatable properties must be backed by dependency properties.
        public static readonly DependencyProperty CenterProperty =
            DependencyProperty.Register("Center", typeof(Point), typeof(PieSlice),
                new PropertyMetadata(new Point(100, 100), OnPropertyChanged));
        public static readonly DependencyProperty RadiusProperty =
            DependencyProperty.Register("Radius", typeof(double), typeof(PieSlice),
                new PropertyMetadata(100.0, OnPropertyChanged));
        public static readonly DependencyProperty StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(PieSlice),
                new PropertyMetadata(0.0, OnPropertyChanged)); // measured in degrees
        public static readonly DependencyProperty SweepAngleProperty =
            DependencyProperty.Register("SweepAngle", typeof(double), typeof(PieSlice),
                new PropertyMetadata(90.0, OnPropertyChanged)); // measured in degrees
        public Point Center
            set { SetValue(CenterProperty, value); }
            get { return (Point)GetValue(CenterProperty); }
        public double Radius
            set { SetValue(RadiusProperty, value); }
            get { return (double)GetValue(RadiusProperty); }
        public double StartAngle
            set { SetValue(StartAngleProperty, value); }
            get { return (double)GetValue(StartAngleProperty); }
        public double SweepAngle
            set { SetValue(SweepAngleProperty, value); }
            get { return (double)GetValue(SweepAngleProperty); }
        public PieSlice()
            pathFigure = new PathFigure { IsClosed = true };
            lineSegment = new LineSegment();
            arcSegment = new ArcSegment { SweepDirection = SweepDirection.Clockwise };
            PathGeometry pathGeometry = new PathGeometry();
            this.Data = pathGeometry;
        private static void OnPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
            (obj as PieSlice).UpdateValues();
        // UpdateValues is called whenever any of the custom properties (Center, Radius
        // StartAngle, SweepAngle) changes.
        private void UpdateValues()
            pathFigure.StartPoint = this.Center;
            double x = this.Center.X + this.Radius * Math.Sin(Math.PI * this.StartAngle / 180);
            double y = this.Center.Y - this.Radius * Math.Cos(Math.PI * this.StartAngle / 180);
            lineSegment.Point = new Point(x, y);
            x = this.Center.X + this.Radius * Math.Sin(Math.PI * (this.StartAngle +
                                                                  this.SweepAngle) / 180);
            y = this.Center.Y - this.Radius * Math.Cos(Math.PI * (this.StartAngle +
                                                                  this.SweepAngle) / 180);
            arcSegment.Point = new Point(x, y);
            arcSegment.IsLargeArc = this.SweepAngle >= 180;
            arcSegment.Size = new Size(this.Radius, this.Radius);


<Page x:Class="TestApp.MainPage"
        <local:PieSlice x:Name="PieSlice1"
                        Center="300,300" Radius="150"
                        Stroke="Blue" StrokeThickness="2" Fill="Wheat" />
                    <DoubleAnimation Storyboard.TargetName="PieSlice1"
                                     From="1" To="359" Duration="0:0:5"
                                     RepeatBehavior="Forever" />


notes/uwp/customcontrols.txt · Last modified: 2020/08/26 (external edit)