Create a custom control (a.k.a. templated control) in Visual Studio:
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:
Generic.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TestApp"> <Style TargetType="local:SimpleControl"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:SimpleControl"> <Grid Height="52" BorderBrush="Blue" BorderThickness="1" Padding="8"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock x:Name="SimpleLabel" Grid.Column="0" Margin="0,0,4,0" VerticalAlignment="Center" /> <TextBox x:Name="SimpleTextBox" Grid.Column="1" Foreground="Gainsboro" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
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:
GradientButton.xaml
<Button x:Class="TestApp.GradientButton" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Button.Background> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Offset="0" Color="{x:Bind ColorBegin}" /> <GradientStop Offset="1" Color="{x:Bind ColorEnd}" /> </LinearGradientBrush> </Button.Background> </Button>
GradientButton.xaml.cs
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() { this.InitializeComponent(); } } }
MainPage.xaml uses the GradientButton:
<Page x:Class="TestApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TestApp"> <Grid> <local:GradientButton Foreground="White" Content="Test Button" ColorBegin="Red" ColorEnd="Blue" /> </Grid> </Page>
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 }; pathFigure.Segments.Add(lineSegment); pathFigure.Segments.Add(arcSegment); PathGeometry pathGeometry = new PathGeometry(); pathGeometry.Figures.Add(pathFigure); this.Data = pathGeometry; UpdateValues(); } 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); } } }
Usage:
<Page x:Class="TestApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TestApp"> <Grid> <local:PieSlice x:Name="PieSlice1" Center="300,300" Radius="150" Stroke="Blue" StrokeThickness="2" Fill="Wheat" /> </Grid> <Page.Triggers> <EventTrigger> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="PieSlice1" Storyboard.TargetProperty="SweepAngle" EnableDependentAnimation="True" From="1" To="359" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Page.Triggers> </Page>
Links: