WPF Reusability Factor – User Controls & Custom Controls

There is always seen a perplexity/confusion exist in terms of User Controls and Custom Controls in regards to WPF. They both work alike in terms of reusability, like you can reuse them over and over, again and again. But there are also finite number of differences between them. And in today's post I’ll highlight them with the help of examples.

A User Control is actually a composite of other UI controls, that means it is composing it with the help of other controls and that can be styled and reused as needed. You compose the existing controls in a User Control using XAML like we do in case of Window or so. And the interaction is done in the code behind, if needed. User Control is derived from a ContentControl (Figure-1). That means it Can only have one Child by nature that you set with the help of its content property. In order to compose with more elements, you need to have a Composite Control like a Panel as its root, like we see in case of compositing the Window itself.

Lets now turn our focus to the Custom Control. By Custom Control we mean it is customizable by the end user/consumer. Lets see how! You derive it from Control or ContentControl or other rich controls like RadioButton control or checkbox control etc. It takes part in the themes as well as skinning mechanism for the end users/consumers with the help of Generic theme defined under Themes/Generic.xaml, where you redefine the ControlTemplate or Visual Tree for this custom control and also provide with a default style as needed. Custom controls provides a much more flexible model in terms of customization as well as reusability as compared to User Controls. If the consumer wants to apply a different theme, all he needs to do is to change the control-template/style of that custom control and that's it, it is customized.

UserControls-CustomControls Figure-1

I’ll explain you all these concepts with the help of a concrete example. Here is how the Project looks like:

image Figure-2

I have created a MainWindow that is composed of a Grid that has two rows. First Row contains a Custom Control “GlassRadioControl” and Row#2 contains a User Control (MessageBoxControl) that mimics more like a MessageWindow or so. Class Hierarchy is shown in the Figure-1 above. Here is the XAML for it.

Code Snippet
  1. <Window x:Class="WpfTestApp.MainWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:local="clr-namespace:WpfTestApp"
  5.     Title="[Custom Control] vs [User Control]" Height="300" Width="500">
  6.     <Window.Resources>
  7.         <Style x:Key="MessageBoxControlStyle1" TargetType="{x:Type local:MessageBoxControl}">
  8.             <Setter Property="Background" Value="LightGreen" />
  9.             <Setter Property="BorderBrush" Value="Gray" />
  10.             <Setter Property="BorderThickness" Value="2" />
  11.             <Setter Property="Template">
  12.                 <Setter.Value>
  13.                     <ControlTemplate TargetType="{x:Type local:MessageBoxControl}">
  14.                         <Border SnapsToDevicePixels="true"
  15.                             Background="{TemplateBinding Background}"
  16.                             BorderBrush="{TemplateBinding BorderBrush}"
  17.                             BorderThickness="{TemplateBinding BorderThickness}"
  18.                             Padding="{TemplateBinding Padding}">
  19.                         <ContentPresenter
  20.                             HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  21.                             VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
  22.                             SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
  23.                         </Border>
  24.                     </ControlTemplate>
  25.                 </Setter.Value>
  26.             </Setter>
  27.         </Style>
  28.     </Window.Resources>
  29.     <Grid Margin="5" Background="Beige">
  30.         <Grid.RowDefinitions>
  31.             <RowDefinition Height="57" />
  32.             <RowDefinition />
  33.         </Grid.RowDefinitions>
  34.         <GroupBox Header="Custom Control's Example" Margin="3">
  35.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >
  36.                 <local:GlassRadioControl DockPanel.Dock="Top" IsChecked="True" LeftColumnWidth="77" />
  37.                 <local:GlassRadioControl DockPanel.Dock="Top" IsRadioChecked="{x:Null}" LeftColumnWidth="77"/>
  38.                 <local:GlassRadioControl IsRadioChecked="False" LeftColumnWidth="77"/>
  39.             </StackPanel>
  40.         </GroupBox>
  41.         <GroupBox Grid.Row="1" Header="User Control's Example" Margin="3">
  42.             <local:MessageBoxControl Message="Hello World"
  43.                 Style="{DynamicResource MessageBoxControlStyle1}" />
  44.         </GroupBox>
  45.     </Grid>
  46. </Window>


Here is the output of the User Control’s:

image Figure-4

and the XAML is shown below.

Code Snippet
  1. <UserControl x:Class="WpfTestApp.MessageBoxControl"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  5.     <Grid>
  6.         <Grid.RowDefinitions>
  7.             <RowDefinition Height="*"/>
  8.             <RowDefinition Height="37"/>
  9.         </Grid.RowDefinitions>
  10.         <Border BorderThickness="1" Opacity="0.75">
  11.             <TextBlock x:Name="MessageText" Text="Message here" Margin="2" VerticalAlignment="Center" />
  12.         </Border>
  13.         <Border Grid.Row="1" Background="LightGray" Opacity="0.65" >
  14.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="5">
  15.                 <Button Content="Cancel" Margin="1" Width="100" Click="ButtonCancelClick" />
  16.                 <Button Content="OK" IsDefault="True" Click="ButtonOkClick" Margin="1" Width="100" />
  17.             </StackPanel>
  18.         </Border>
  19.     </Grid>
  20. </UserControl>


Basically we composed a TextBlock control and Two button controls and lay them out in the Grid and we have the UserControl ready. And the instance is created at line 42, Figure-3 above.

Lets now move on to the Custom Control, GlassRadioControl. This Control is derived from RadioButton (See Figure-1). And what’s the rationale behind extending the RadioButton, is actually we wanted the same behavior as of RadioButton but it should have a look and feel of a specialized split button. This could have done by using just the Control as a base with a little more hard work. Here is what we wanted to accomplish in this Custom-Control:

1) When it is selected, it should have an outline around it.
2) The button has to be split to show text on your Left and visual representation on the right, that is based on some state. Like When it is ok to Run, shows a Tick mark, while if its in progress shows revolving/animated Arrowed-Circle and when there is an error shows a cross, etc. Here is how it looks like for different states:



In order to achieve this, I have modified the control template in the Themes/Generic.xaml, and here is how it looks like (see line 69):

Code Snippet
  1. <ResourceDictionary
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:local="clr-namespace:WpfTestApp">
  6.     <Style x:Key="CrossPathStyle" TargetType="{x:Type Path}" >
  7.         <Setter Property="Data" Value="M0,0L10,10 M10,0L0,10"/>
  8.         <Setter Property="Stroke" Value="#FFFF0000"/>
  9.         <Setter Property="StrokeThickness" Value="1.25"/>
  10.         <Setter Property="StrokeStartLineCap" Value="Round"/>
  11.         <Setter Property="StrokeEndLineCap" Value="Round"/>
  12.         <Setter Property="StrokeLineJoin" Value="Round"/>
  13.         <Setter Property="RenderTransformOrigin" Value="0.479,0.475"/>
  14.         <Setter Property="Opacity" Value="0.75"/>
  15.         <Setter Property="RenderTransform">
  16.             <Setter.Value>
  17.                 <TransformGroup>
  18.                     <ScaleTransform ScaleX="1" ScaleY="1"/>
  19.                     <SkewTransform AngleX="0" AngleY="0"/>
  20.                     <RotateTransform Angle="0"/>
  21.                     <TranslateTransform X="0" Y="0"/>
  22.                 </TransformGroup>
  23.             </Setter.Value>
  24.         </Setter>
  25.     </Style>
  27.     <Style x:Key="TickPathStyle" TargetType="{x:Type Path}">
  28.         <Setter Property="Data" Value="M0,5L3,10 10,0"/>
  29.         <Setter Property="Stroke" Value="#FF90EE90"/>
  30.         <Setter Property="StrokeThickness" Value="1.25"/>
  31.         <Setter Property="StrokeStartLineCap" Value="Round"/>
  32.         <Setter Property="StrokeEndLineCap" Value="Round"/>
  33.         <Setter Property="StrokeLineJoin" Value="Round"/>
  34.         <Setter Property="RenderTransformOrigin" Value="0.479,0.475"/>
  35.         <Setter Property="Opacity" Value="0.75"/>
  36.         <Setter Property="RenderTransform">
  37.             <Setter.Value>
  38.                 <TransformGroup>
  39.                     <ScaleTransform ScaleX="1" ScaleY="1"/>
  40.                     <SkewTransform AngleX="0" AngleY="0"/>
  41.                     <RotateTransform Angle="0"/>
  42.                     <TranslateTransform X="0" Y="0"/>
  43.                 </TransformGroup>
  44.             </Setter.Value>
  45.         </Setter>
  46.     </Style>
  48.     <Style x:Key="ArrowRoundPath" TargetType="{x:Type Path}">
  49.         <Setter Property="RenderTransformOrigin" Value="0.556,0.5" />
  50.         <!--<Setter Property="Margin" Value="0,0,0,10" />-->
  51.         <Setter Property="Stretch" Value="Uniform" />
  52.         <Setter Property="Stroke" Value="#BF0000FF" />
  53.         <Setter Property="StrokeThickness" Value="2.75" />
  54.         <Setter Property="StrokeMiterLimit" Value="2.5" />
  55.         <Setter Property="Data" Value="M6.8783809,21.019825 C5.7961721,19.096961 5.1789828,16.872546 5.1789828,14.499944 5.1789828,7.228976 10.982436,1.333335 18.142628,1.333335 25.30132,1.333335 31.104814,7.228976 31.104814,14.499944 31.104814,21.771013 25.30132,27.666664 18.142628,27.666664 15.331431,27.666664 12.729834,26.756829 10.606237,25.213419 M9.1658801,15.547488 L1.333335,19.323817 7.9703076,22.081687 9.1658801,15.547488 z" />
  56.         <Setter Property="RenderTransform">
  57.             <Setter.Value>
  58.                 <TransformGroup>
  59.                     <ScaleTransform ScaleX="-1" ScaleY="1"/>
  60.                     <SkewTransform AngleX="0" AngleY="0"/>
  61.                     <RotateTransform Angle="0"/>
  62.                     <TranslateTransform X="0" Y="0"/>
  63.                 </TransformGroup>
  64.             </Setter.Value>
  65.         </Setter>
  66.         <Setter Property="Opacity" Value="0.75"/>
  67.     </Style>
  69.     <ControlTemplate x:Key="GlassRadioTemplate" TargetType="{x:Type local:GlassRadioControl}">
  70.         <ControlTemplate.Resources>
  71.             <Storyboard x:Key="OnLoaded1">
  72.                 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="path2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" RepeatBehavior="Forever">
  73.                     <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
  74.                     <SplineDoubleKeyFrame KeyTime="00:00:01.0000000" Value="360"/>
  75.                 </DoubleAnimationUsingKeyFrames>
  76.             </Storyboard>
  77.             <Storyboard x:Key="Timeline1">
  78.                 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="glow" Storyboard.TargetProperty="(UIElement.Opacity)">
  79.                     <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
  80.                 </DoubleAnimationUsingKeyFrames>
  81.             </Storyboard>
  82.             <Storyboard x:Key="Timeline2">
  83.                 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="glow" Storyboard.TargetProperty="(UIElement.Opacity)">
  84.                     <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
  85.                 </DoubleAnimationUsingKeyFrames>
  86.             </Storyboard>
  87.         </ControlTemplate.Resources>
  88.         <Border BorderBrush="#FFFFFFFF" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
  89.             <Border x:Name="border" Background="#7F000000" BorderBrush="#FF000000" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
  90.                 <Grid>
  91.                     <Grid.RowDefinitions>
  92.                         <RowDefinition Height="0.55*"/>
  93.                         <RowDefinition Height="0.45*" />
  94.                     </Grid.RowDefinitions>
  95.                     <Border Opacity="0" HorizontalAlignment="Stretch" x:Name="glow" Width="Auto" Grid.RowSpan="2" CornerRadius="4,4,4,4">
  96.                         <Border.Background>
  97.                             <RadialGradientBrush>
  98.                                 <RadialGradientBrush.RelativeTransform>
  99.                                     <TransformGroup>
  100.                                         <ScaleTransform ScaleX="1.702" ScaleY="2.243"/>
  101.                                         <SkewTransform AngleX="0" AngleY="0"/>
  102.                                         <RotateTransform Angle="0"/>
  103.                                         <TranslateTransform X="-0.368" Y="-0.152"/>
  104.                                     </TransformGroup>
  105.                                 </RadialGradientBrush.RelativeTransform>
  106.                                 <GradientStop Color="#B28DBDFF" Offset="0"/>
  107.                                 <GradientStop Color="#008DBDFF" Offset="1"/>
  108.                             </RadialGradientBrush>
  109.                         </Border.Background>
  110.                     </Border>
  111.                     <Grid Grid.Row="0" Grid.RowSpan="2" Margin="0">
  112.                         <Grid.ColumnDefinitions>
  113.                             <ColumnDefinition Width="{TemplateBinding LeftColumnWidth}" />
  114.                             <ColumnDefinition Width="{TemplateBinding RightColumnWidth}" />
  115.                         </Grid.ColumnDefinitions>
  117.                         <ContentPresenter Grid.Column="0" Margin="3,0,0,0" x:Name="contentPresenter"
  118.                                 Content="{TemplateBinding Content}"
  119.                                 ContentTemplate="{TemplateBinding ContentTemplate}"
  120.                                 HorizontalAlignment="Center"
  121.                                 VerticalAlignment="Center" />
  123.                         <Border Grid.Column="1" BorderBrush="Silver" BorderThickness="1.5,0,0,0" />
  124.                         <Border Grid.Column="1" BorderBrush="Black" BorderThickness="1.25,0,0,0" />
  125.                         <Viewbox Grid.Column="1" x:Name="view1" Visibility="Visible" Margin="5" RenderTransformOrigin="0.5,0.5">
  126.                             <Path x:Name="path" Style="{StaticResource CrossPathStyle}" />
  127.                         </Viewbox>
  128.                         <Viewbox Grid.Column="1" x:Name="view2" Visibility="Collapsed" Margin="2">
  129.                             <Path x:Name="path2" Style="{StaticResource ArrowRoundPath}"/>
  130.                         </Viewbox>
  131.                     </Grid>
  132.                     <Border HorizontalAlignment="Stretch" Margin="0,0,0,0" x:Name="shine" Width="Auto" CornerRadius="4,4,0,0">
  133.                         <Border.Background>
  134.                             <LinearGradientBrush EndPoint="0.494,0.889" StartPoint="0.494,0.028">
  135.                                 <GradientStop Color="#99FFFFFF" Offset="0"/>
  136.                                 <GradientStop Color="#33FFFFFF" Offset="1"/>
  137.                             </LinearGradientBrush>
  138.                         </Border.Background>
  139.                     </Border>
  140.                 </Grid>
  141.             </Border>
  142.         </Border>
  143.         <ControlTemplate.Triggers>
  144.             <Trigger Property="IsChecked" Value="True">
  145.                 <Setter Property="BorderBrush" TargetName="border" Value="#FFFFA500"/>
  146.             </Trigger>
  147.             <Trigger Property="IsPressed" Value="True">
  148.                 <Setter Property="Opacity" TargetName="shine" Value="0.4"/>
  149.                 <Setter Property="Background" TargetName="border" Value="#CC000000"/>
  150.                 <Setter Property="Visibility" TargetName="glow" Value="Hidden"/>
  151.             </Trigger>
  152.             <Trigger Property="IsMouseOver" Value="True">
  153.                 <Trigger.EnterActions>
  154.                     <BeginStoryboard Storyboard="{StaticResource Timeline1}"/>
  155.                 </Trigger.EnterActions>
  156.                 <Trigger.ExitActions>
  157.                     <BeginStoryboard x:Name="Timeline2_BeginStoryboard" Storyboard="{StaticResource Timeline2}"/>
  158.                 </Trigger.ExitActions>
  159.             </Trigger>
  160.             <Trigger Property="IsRadioChecked" Value="True">
  161.                 <Setter Property="Content" Value="{Binding Path=TickContentText}" />
  162.                 <Setter TargetName="view2"
  163.                             Property="Visibility"
  164.                             Value="Collapsed" />
  165.                 <Setter TargetName="path"
  166.                             Property="Style"
  167.                             Value="{StaticResource TickPathStyle}" />
  168.             </Trigger>
  170.             <Trigger Property="IsRadioChecked" Value="False">
  171.                 <!--<Setter TargetName="contentPresenter" Property="Content" Value="{Binding Path=CrossContentText}" />-->
  172.                                 <Setter Property="Content" Value="{Binding Path=CrossContentText}" />
  173.             </Trigger>
  175.             <Trigger Property="IsRadioChecked" Value="{x:Null}">
  176.                 <Setter Property="Content" Value="{Binding Path=ProgressContentText}" />
  177.                 <Setter TargetName="view2"
  178.                             Property="Visibility"
  179.                             Value="Visible" />
  180.                 <Setter TargetName="view1"
  181.                             Property="Visibility"
  182.                             Value="Collapsed" />
  183.             </Trigger>
  184.             <!--<EventTrigger RoutedEvent="FrameworkElement.Loaded">
  185.                 <BeginStoryboard Storyboard="{StaticResource OnLoaded1}"/>
  186.             </EventTrigger>-->
  187.         </ControlTemplate.Triggers>
  188.     </ControlTemplate>
  190.     <Style TargetType="{x:Type local:GlassRadioControl}">
  191.         <Setter Property="Template" Value="{StaticResource GlassRadioTemplate}"/>
  192.     </Style>
  194.     <!--<Style TargetType="{x:Type local:GlassRadioControl}">
  195.         <Setter Property="Template">
  196.             <Setter.Value>
  197.                 <ControlTemplate TargetType="{x:Type local:GlassRadioControl}">
  198.                     <Border Background="{TemplateBinding Background}"
  199.                             BorderBrush="{TemplateBinding BorderBrush}"
  200.                             BorderThickness="{TemplateBinding BorderThickness}">
  201.                     </Border>
  202.                 </ControlTemplate>
  203.             </Setter.Value>
  204.         </Setter>
  205.     </Style>-->
  206. </ResourceDictionary>


In this Custom Control, GlassRadioControl, there are three things noticeable. If you look at the XAML (Figure-7), I have created three styles for the paths and referred them under the ControlTemplate section of the Generic.xaml, resource dictionary under. The Control Template’s Resource section contains the animation logic for the circular motion of the Arrow. Also I took advantage of Triggers in the Control Template to obtain the Conditional Logic for the control, like when it is selected, a Border will be drawn etc.
And here is the Code behind for it. If you look at the code, we defined IsRadioChecked Dependency Property, that is responsible for states, like running or error states or so while LeftColumnWidth and RightColumnWidth Dependency properties are responsible for Setting the Left and Right Width of the Split sections. Also you can set the Ticked Content using TickContentText Property, CrossContentText Property when it sees an Error state as well as the ProgressContentText property for showing the running state.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. using System.Windows.Media.Animation;
  16. namespace WpfTestApp
  17. {
  18.     /// <summary>
  19.     /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
  20.     ///
  21.     /// Step 1a) Using this custom control in a XAML file that exists in the current project.
  22.     /// Add this XmlNamespace attribute to the root element of the markup file where it is
  23.     /// to be used:
  24.     ///
  25.     ///     xmlns:MyNamespace="clr-namespace:WpfTestApp"
  26.     ///
  27.     ///
  28.     /// Step 1b) Using this custom control in a XAML file that exists in a different project.
  29.     /// Add this XmlNamespace attribute to the root element of the markup file where it is
  30.     /// to be used:
  31.     ///
  32.     ///     xmlns:MyNamespace="clr-namespace:WpfTestApp;assembly=WpfTestApp"
  33.     ///
  34.     /// You will also need to add a project reference from the project where the XAML file lives
  35.     /// to this project and Rebuild to avoid compilation errors:
  36.     ///
  37.     ///     Right click on the target project in the Solution Explorer and
  38.     ///     "Add Reference"->"Projects"->[Browse to and select this project]
  39.     ///
  40.     ///
  41.     /// Step 2)
  42.     /// Go ahead and use your control in the XAML file.
  43.     ///
  44.     ///     <MyNamespace:GlassRadioControl/>
  45.     ///
  46.     /// </summary>
  47.     public partial class GlassRadioControl : RadioButton
  48.     {
  49.         static GlassRadioControl()
  50.         {
  51.             try
  52.             {
  53.                 DefaultStyleKeyProperty.OverrideMetadata(typeof(GlassRadioControl),
  54.                     new FrameworkPropertyMetadata(typeof(GlassRadioControl)));
  55.             }
  56.             catch
  57.             {
  59.             }
  60.         }
  62.         public static readonly DependencyProperty IsRadioCheckedProperty = DependencyProperty.Register("IsRadioChecked",
  63.             typeof(bool?),
  64.             typeof(GlassRadioControl),
  65.             new FrameworkPropertyMetadata(true,
  66.             new PropertyChangedCallback(RadioCheckedPropertyChangedCallBack)));
  68.         public bool? IsRadioChecked
  69.         {
  70.             get { return (bool?)GetValue(IsRadioCheckedProperty); }
  71.             set { SetValue(IsRadioCheckedProperty, value); }
  72.         }
  74.         static void RadioCheckedPropertyChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
  75.         {
  77.         }
  79.         public static readonly DependencyProperty LeftColumnWidthProperty = DependencyProperty.Register("LeftColumnWidth",
  80.             typeof(GridLength),
  81.             typeof(GlassRadioControl),
  82.             new FrameworkPropertyMetadata(new GridLength(0.25, GridUnitType.Auto),
  83.             new PropertyChangedCallback(LeftColumnWidthPropertyChangedCallBack)));
  85.         public GridLength LeftColumnWidth
  86.         {
  87.             get { return (GridLength)GetValue(LeftColumnWidthProperty); }
  88.             set { SetValue(LeftColumnWidthProperty, value); }
  89.         }
  91.         static void LeftColumnWidthPropertyChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
  92.         {
  93.         }
  95.         public static readonly DependencyProperty RightColumnWidthProperty = DependencyProperty.Register("RightColumnWidth",
  96.              typeof(GridLength),
  97.              typeof(GlassRadioControl),
  98.              new FrameworkPropertyMetadata(new GridLength(0.25, GridUnitType.Auto),
  99.              new PropertyChangedCallback(RightColumnWidthPropertyChangedCallBack)));
  101.         public GridLength RightColumnWidth
  102.         {
  103.             get { return (GridLength)GetValue(RightColumnWidthProperty); }
  104.             set { SetValue(RightColumnWidthProperty, value); }
  105.         }
  108.         static void RightColumnWidthPropertyChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
  109.         {
  110.         }
  112.         public static readonly DependencyProperty TickContentTextProperty = DependencyProperty.Register("TickContentText",
  113.             typeof(string),
  114.             typeof(GlassRadioControl),
  115.             new FrameworkPropertyMetadata("Run",
  116.             new PropertyChangedCallback(TickContentTextPropertyChangedCallBack)));
  118.         public string TickContentText
  119.         {
  120.             get { return (string)GetValue(TickContentTextProperty); }
  121.             set { SetValue(TickContentTextProperty, value); }
  122.         }
  124.         static void TickContentTextPropertyChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
  125.         {
  126.         }
  128.         public static readonly DependencyProperty CrossContentTextProperty = DependencyProperty.Register("CrossContentText",
  129.             typeof(string),
  130.             typeof(GlassRadioControl),
  131.             new FrameworkPropertyMetadata("Error",
  132.             new PropertyChangedCallback(CrossContentTextPropertyChangedCallBack)));
  134.         public string CrossContentText
  135.         {
  136.             get { return (string)GetValue(CrossContentTextProperty); }
  137.             set { SetValue(CrossContentTextProperty, value); }
  138.         }
  140.         static void CrossContentTextPropertyChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
  141.         {
  142.         }
  144.         public static readonly DependencyProperty ProgressContentTextProperty = DependencyProperty.Register("ProgressContentText",
  145.             typeof(string),
  146.             typeof(GlassRadioControl),
  147.             new FrameworkPropertyMetadata("Runing",
  148.             new PropertyChangedCallback(ProgressContentTextPropertyChangedCallBack)));
  150.         public string ProgressContentText
  151.         {
  152.             get { return (string)GetValue(ProgressContentTextProperty); }
  153.             set { SetValue(ProgressContentTextProperty, value); }
  154.         }
  156.         static void ProgressContentTextPropertyChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
  157.         {
  158.         }
  160.         public GlassRadioControl()
  161.         {
  162.             // this is important if you want to bind the properties in the triggers or so...
  163.             this.DataContext = this;
  164.         }
  166.         public override void OnApplyTemplate()
  167.         {
  168.             base.OnApplyTemplate();
  170.             Storyboard storyboard = (Storyboard)this.Template.Resources["OnLoaded1"];
  171.             storyboard.Begin(this, this.Template);
  172.         }
  174.     }
  175. }


Once this Custom Control is developed, you may reuse it over and over and again and again, as you can see Lines 36-38, Figure-3.
Now, the purpose of the test application is to show you how we consume both Custom Controls and User Controls and how they can be reused and customized for our needs. Here is the final output for it:



That's all for now, I’ll elaborate the concept of custom controls in a more like a tutorial, when I cover the Control Templates in context to Another Reusability Factor i.e Styling, Themes and Skinning  , So stay tuned. Enjoy :)

Download File - Sample Project

If you enjoyed reading this blog, leave your valuable feedback and consider subscribing to the RSS feed. You can also subscribe to it by email. Also, you can follow me on Twitter. Thank you!

Technorati Tags: ,,

Comments (26) -

anabolic again review
2/19/2016 2:26:39 AM #

I don't know whether it's just me or if perhaps everyone else experiencing problems with your site. It appears as though some of the written text on your content are running off the screen. Can somebody else please comment and let me know if this is happening to them too? This might be a problem with my browser because I've had this happen previously. Cheers

2/19/2016 4:53:36 AM #

Hi there i am kavin, its my first occasion to commenting anywhere, when i read this article i thought i could also create comment due to this brilliant article.  My web blog;  market - http://firsturl.de/H4rYiGp

Max Slim Supplement
2/24/2016 3:01:31 AM #

Hey would you mind letting me know which hosting company you're utilizing? I've loaded your blog in 3 completely different internet browsers and I must say this blog loads a lot faster then most. Can you suggest a good web hosting provider at a honest price? Cheers, I appreciate it!

3/4/2016 2:33:15 AM #

Terimakasih atas informasinya nih bos, kunjungi balik website gw dong http:// www.obatulkuslambung.cf - http://www.obatulkuslambung.cf

3/4/2016 3:31:38 PM #

you have best website i love it

3/5/2016 8:15:36 AM #

Hello! I've been following your web site for some time now and finally got the bravery to go ahead and give you a shout out from  New Caney Texas! Just wanted to tell you keep up the excellent work!  Here is my weblog  Casie - Compunetixinc.com/.../netsoltrademark.php

3/13/2016 1:22:47 PM #

I think this is among the most vital information for me. And i am glad reading your article. But want to remark on few general things, The site style is perfect, the articles is really nice : D. Good job, cheers

3/13/2016 10:43:12 PM #

The Garmin connect website was upgraded 105% since my first Garmin GPS watch the Forerunner 201.  Review my web-site -  Russel - www.lcsroofing-cladding.co.uk/.../

Beauty Parlours in Delhi
4/15/2016 9:04:10 AM #

Got just what I was looking. Enjoyed and knowledgable!

4/22/2016 2:54:51 AM #

Hi, all the time i used to check web site posts here in the early hours in the dawn, for the reason that i enjoy to learn more and more.

Online video marketing
5/14/2016 3:00:29 AM #

I was suggested this website by my cousin. I am not sure whether this post is written by him as no one else know such detailed about my difficulty. You're wonderful! Thanks!  My web page -  Online video marketing - www.ncbi.nlm.nih.gov/.../

Juan Manuel
7/15/2016 12:22:41 PM #

At this moment I am ready to do my breakfast, after having my breakfast coming over again to read other news.

Check out my web site ...  Juan Manuel - http://github.com/obattipes85

7/21/2016 11:38:47 PM #

I enjoy the efforts you have put in this, thank you for all the great content.

Contoh Text Report Tentang Binatang
7/28/2016 5:06:45 AM #

I got this site from my pal who told me regarding this web site and now this time I am browsing this site and reading very informative posts at this time.

Also visit my web blog -  Contoh Text Report Tentang Binatang - http://zon9.xyz/lPlw0

Michal E. Dowdy
8/26/2016 8:22:30 AM #

I love your blog. You really have introduced a very good topic here for us. Thanks for that. I will come back to be updated.

payday loans uk
9/27/2016 9:27:42 AM #

I just like the valuable information you provide in your articles. I'll bookmark your blog and check once more right here frequently. I am slightly certain I will be informed many new stuff proper right here! Best of luck for the following!

rolex replica
9/28/2016 9:28:37 AM #

The endure James Bond http://www.replicasonline.co.uk that went to auction, a Submariner beat by Sir Roger Moore in the 1973 blur Live and Let Die, fetched a baronial $372,000. But this pre-Daytona is so appropriate that Artcurial will not about accommodate a sales appraisal for http://www.web-farm.co.uk it. While Submariners are associated with James Bond characters and acclaimed actors such as Sean Connery and Moore, our present archetype is http://www.replicahause.me.uk the alone alarm ref.

fiverr instagram followers
10/22/2016 3:31:59 PM #

You can either select a selected voice actor, or maintain an audition with up to three candidates.  Feel free to surf to my blog -  fiverr instagram followers - freecelebritywallpapers.com/profile/xgushantae

I have read so many content about the blogger lovers however this article is really a pleasant paragraph, keep it up.

Disability Support Worker
11/11/2016 8:47:48 AM #

WOW just what I was searching for. Came here by searching for Coach

Wonderful work! This is the type of info that should be shared around the net. Shame on Google for no longer positioning this submit higher! Come on over and seek advice from my site . Thank you =)

แว่นตา 500
11/17/2016 4:13:30 AM #

I do not know if it's just me or if perhaps everybody else experiencing problems with your site. It seems like some of the written text on your content are running off the screen. Can someone else please comment and let me know if this is happening to them too? This could be a problem with my web browser because I've had this happen before. Thank you

bioxcin quantum forum
11/24/2016 4:40:39 PM #

Saç dökülmesine karşı olumlu etkileri olan birçok maddeyi.

After I initially left a comment I seem to have clicked the -Notify me when new comments are added- checkbox and from now on whenever a comment is added I receive 4 emails with the same comment. Is there a means you are able to remove me from that service? Cheers!

12/13/2016 1:39:48 PM #

Cloud based backup agencies usually give a hoard of file storage services that works well like local folders and in addition permits the users to easily create, share and edit the files through them.  With the Metro user interface for use with Windows 8, touch friendly designs are not tied to cellular devices.  The last but absolutely not minimal factor is the integration of lotus notes with all the World Wide Web.

file my citizens claim
12/15/2016 6:23:12 AM #

Right here is the right website for everyone who wants to understand this topic. You know a whole lot its almost tough to argue with you (not that I actually will need to…HaHa). You definitely put a fresh spin on a topic which has been discussed for years. Great stuff, just wonderful!

Terrific article! That is the kind of info that are meant to be shared around the net. Shame on Google for no longer positioning this submit higher! Come on over and talk over with my web site . Thanks =)

Marvelous, what a web site it is! This blog presents valuable facts to us, keep it up.  Take a look at my blog ...  used aluminum jon boats for sale in texas - http://www.lh-st.com/cart.aspx?returnurl=http://www.honningvin.dk/.../simple-actions-how-get-great-vehicle-insurance

cccam cline generator download
1/9/2017 9:14:55 PM #

Obviously, CCcam One wants picture optimization as it could save up to 203.1 kB or 26% of the unique quantity.

Alfredo Willof
1/12/2017 12:25:41 AM #

You actually make it seem so easy along with your presentation but I in finding this topic to be actually one thing that I believe I might never understand. It seems too complicated and extremely broad for me. I am looking forward in your subsequent put up, I'll attempt to get the hold of it!

my web-site ...  Alfredo Willof - www.nature.com/protocolexchange/labgroups/366729

1/27/2017 8:30:35 PM #

Buy Anabolic Steroids Usa - Oral Steroids For Sale In Usa !,Steroids,steroids for sale,steroids definition,steroids in sports,steroids online,steroids for back pain,steroids in baseball,steroids effects,steroid sell,steroid seller,steroid seller reviews,anabolic steroid seller,online steroid seller,anabolic steroid sell,steroid pills for sell,sell steroids,sell anabolic steroid

Chicago Limo Service
1/30/2017 5:20:45 PM #

Awesome! Its actually amazing paragraph, I have got much clear idea concerning from this piece of writing.

מסגריה בתל אביב
2/2/2017 1:47:59 PM #

Bramah manufactures locks for domestic and commercial use. That is absolutely true, since you will be able to improve in each aspect. This is one of the miracles an auto locksmith can perform.  Feel free to surf to my web site  מסגריה בתל אביב - www.webcontroldirectory.com/.../8075-tel-aviv-locksmith-products---the-basics

xarelto side effects depression
2/3/2017 12:20:53 PM #

This paragraph gives clear idea designed for the new viewers of blogging, that genuinely how to do blogging and site-building.  my weblog;  xarelto side effects depression - cookingandcaring.com/2016/01/25/nazi-goreng/

2/6/2017 5:17:48 AM #

Thanks for sharing your thoughts on Кононов Виталий Юрьевич. Regards  My web site -  Professionali.Ru - professionali.ru/~vitalij-jurjevich-kononov/

2/10/2017 12:39:42 AM #

I couldn't resist commenting. Very well written!

2/16/2017 11:20:02 AM #

excellent post, very informative. I ponder why the other specialists of this sector do not understand this. You should proceed your writing. I am sure, you have a great readers' base already!

2/24/2017 3:52:45 AM #

Wow, that's what I was searching for, what a material! existing here at this website, thanks admin of this web site.  Feel free to surf to my page ::  http://www.shopping-tirol.at/ - www.shopping-tirol.at/.../suchen.pl

3/1/2017 9:40:32 PM #

I enjoy reading through an article that can make men and women think. Also, many thanks for allowing me to comment!  my blog -  Kaka.Com.Pk - http://kaka.com.pk/user/profile/465512

3/1/2017 10:38:28 PM #

Now I am going away to do my breakfast, later than having my breakfast coming over again to read additional news.  my web site ::  Pigskinzone.com - http://pigskinzone.com/activity/p/486363/

What a stuff of un-ambiguity and preserveness of precious know-how on the topic of unpredicted emotions.  Here is my weblog;  fast approval bad credit check payday loans same day - http://www.opploans.com/

canon eos 500 bc
3/7/2017 12:53:03 AM #

It's not my first time to visit this site, i am visiting this site dailly and obtain pleasant data from here daily.

This is my first time pay a quick visit at here and i am truly impressed to read everthing at alone place.  Feel free to surf to my blog ...  fast approval bad credit check payday loans same day - http://www.instantpaydayloans24.org/

Bonded Contractor
3/13/2017 12:09:08 PM #

My brother suggested I might like this blog. He was entirely right. This publish actually made my day. You can not imagine just how a lot time I had spent for this info! Thank you!  Also visit my weblog -  Bonded Contractor - Ventra.ru/inc/index.php?a=stats&u=shelli6188

pinterest.com login
3/19/2017 10:54:29 AM #

The power of deletion keeps your boards tidy and eradicates images you no longer wish to share together with your friends along with the world at large. To accomplish this, go towards the top of one's screen, hit view and hit show bookmarks bar.   pinterest.com login - knowledgebox.drillutions.com/.../

3/20/2017 10:25:13 PM #

It's not my first time to pay a quick visit this website, i am visiting this web page dailly and obtain pleasant facts from here everyday.  Stop by my homepage;  ansiedad - http://mca-biz.com/como-controlar-ansiedad24804

hello there and thank you for your information – I have certainly picked up something new from right here. I did however expertise several technical issues using this website, as I experienced to reload the website a lot of times previous to I could get it to load correctly. I had been wondering if your web hosting is OK? Not that I'm complaining, but sluggish loading instances times will often affect your placement in google and can damage your high-quality score if advertising and marketing with Adwords. Well I am adding this RSS to my email and can look out for a lot more of your respective interesting content. Make sure you update this again very soon.

This is very fascinating, You are an excessively skilled blogger. I have joined your feed and sit up for searching for more of your magnificent post. Also, I have shared your site in my social networks

PixelBolt review comparison
3/26/2017 8:15:04 AM #

The beauty of this strategic movie is the fact that it is not only aesthetically interesting and packed with information, but it also serves as an instructional video without sounding like a traditional training show.

3/28/2017 6:06:31 AM #

I really like your blog.. very nice colors & theme. Did you design this website yourself or did you hire someone to do it for you? Plz answer back as I'm looking to construct my own blog and would like to find out where u got this from. many thanks

como eliminar la ansiedad
3/29/2017 3:38:10 AM #

If some one needs expert view concerning running a blog after that i advise him/her to pay a quick visit this web site, Keep up the nice work.  my web-site  como eliminar la ansiedad - http://taer-qhtani.com/remedios-caseros-ansiedad/

what year did xarelto come out
3/31/2017 9:34:30 PM #

With havin so much content do you ever run into any issues of plagorism or copyright infringement? My website has a lot of unique content I've either created myself or outsourced but it looks like a lot of it is popping it up all over the internet without my permission. Do you know any techniques to help protect against content from being stolen? I'd truly appreciate it.  Also visit my web page ::  what year did xarelto come out - xareltolawsuit.bitbucket.org/.../...-MO-64086.html

Oh my goodness! Incredible article dude! Thank you, However I am encountering problems with your RSS. I don't know the reason why I am unable to subscribe to it. Is there anyone else having similar RSS problems? Anyone that knows the answer will you kindly respond? Thanks!!  Review my web page;  michael kors watches for women sale - sklim.Or.kr/.../

4/1/2017 4:41:55 PM #

When someone writes an post he/she keeps the image of a user in his/her brain that how a user can be aware of it. Therefore that's why this paragraph is amazing. Thanks!

4/3/2017 2:02:23 AM #

Hello are using Wordpress for your blog platform? I'm new to the blog world but I'm trying to get started and set up my own. Do you require any html coding knowledge to make your own blog? Any help would be greatly appreciated!

Dirty gays
4/3/2017 8:55:17 AM #

You should take part in a contest for one of the best websites online. I will highly recommend this site!  Here is my homepage;  Dirty gays - http://Goodline.pro/

4/7/2017 12:48:36 PM #

Oh my goodness! Impressive article dude! Thank you so much, However I am experiencing issues with your RSS. I don't know why I can't join it. Is there anybody getting similar RSS problems? Anybody who knows the answer will you kindly respond? Thanks!!

4/7/2017 1:18:51 PM #

We're a group of volunteers and opening a new scheme in our community. Your web site offered us with valuable information to work on. You've performed a formidable activity and our whole neighborhood shall be thankful to you.

citibank online login
4/8/2017 3:07:13 AM #

You can return your car on your lender through voluntary repossession. They can and do, by treating the fees being an overhead cost and incorporating them within their prices.   citibank online login - payapps.co.kr/.../index.php

4/11/2017 6:28:16 AM #

I've been browsing online more than 3 hours today, yet I never found any interesting article like yours. It is pretty worth enough for me. Personally, if all website owners and bloggers made good content as you did, the net will be much more useful than ever before.

4/14/2017 12:23:59 PM #

In older versions of AOL Instant Messenger (AIM), users can find people on AIM by while using the "Find.   aolmail.com - tumedicion.com/

citicards reconsideration line
4/15/2017 4:06:20 AM #

A listing of approved credit counselors by state is available with the U. In fact, those who have plastic cards sometimes waste tens, or else hundreds, of dollars buying products which teach you what you are able to learn at no cost online.   citicards reconsideration line - dijongreen.com/

aol mail login
4/16/2017 1:21:41 AM #

You'll be able to a picture which is to be viewed by Gmail friends or alter any of the tastes.   aol mail login - vancouvermusicdirectory.com/author/floyblaze87/

itunes support number
4/17/2017 8:20:54 AM #

i - Tunes is Apple software available for download that offers you access to millions of songs, music videos, television shows, movies, i - Phone applications,. Netflix has quickly moved on the forefront from the movie rental industry which has a combination of DVDs and streaming movies.   itunes support number - rocksandmineralstrader.com/author/junehood72/

4/17/2017 3:49:21 PM #

I think the admin of this website is actually working hard for his website, for the reason that here every stuff is quality based material.

Garment Conveyor Installation
4/19/2017 10:33:09 AM #

I relish, cause I found exactly what I was looking for. You've ended my 4 day long hunt! God Bless you man. Have a great day. Bye  Feel free to surf to my web site ...  Garment Conveyor Installation - Tools.Wmflabs.org/.../index.php

my ebay
4/21/2017 2:55:23 AM #

The day of rest seemed to accomplish the trick and the fever has disappeared now. Maybe not you'd like reading this article, but you can sure get it done in less compared to a week& and even a number of days.   my ebay - http://www.ventechiot.fr/author/kalaveale73/

Yes! Finally something about ติดแก๊ส lpg.

Add comment