I am going to show you how to create and use a user control in
WPF, also known as composite control, using Visual
Studio 2010.
The control I am going to create will be used within a
scheduling application, and it is used to specify the hour and
minute in each day that the scheduler becomes active. When the
scheduling application was first written, the user had to specify
the hour and day in two separate text boxes, like this:

Furthermore, the original application contained no validation
for user input on those text boxes, so users were free to input
values that were not numbers between 1 and 24 or 1 and 60, or not
even numbers at all.
Create the User Control
In Visual Studio 2010, select the appropriate project in your
solution, right-click, choose "Add" then "New Item". From the list
in the Add New Item dialog, select "User Control (WPF)". Provide a
name for the control: "HourMinutePicker.xaml:

We will want to access our new control from the Visual Studio
Toolbox. To add the new control to the Toolbox, first build your
assembly. Then right-click on the Toolbox and select "Choose
Items…". The "Choose Toolbox Items" dialog will open. Select the
"WPF Components" tab. Now click the "Browse" button and browse to
the compiled assembly that contains the control. In my example the
control is part of the executable so I browse to the bin folder and
select my exe file. My control is then listed and I can select
it:

Hint: to see your new control in the Toolbox, you may have to
right-click on the Toolbox and select "Show All".
Obviously you don't have to add your new control to the Toolbox
- you can still use it just by referencing it through its namespace
in your xaml.
For a more detailed description of adding WPF controls to the
Toolbox see:
http://geekswithblogs.net/RockStarCoder/archive/2009/07/15/adding-your-wpf-control-to-the-toolbox.aspx
Add the Constituent Controls
A user control is also known as a composite control
because it may contain other controls, known as constituent
controls. Our hour minute picker will contain some constituent
controls, including two text boxes to enter the hour and minute
respectively, plus some custom spinners to allow the user to
increment or decrement the values (a more sophisticated solution
would create another user control to encapsulate these time
element, and we would re-use this control twice within our
control).
Double-click on the xaml file component of your new control to
open the designer. Open the Visual Studio Toolbox. From the Toolbox
drag three controls onto your designer surface:
- A text box for the hour component
- A text box for the minute component
- A label to act as a separator between the hour and minute
values. Give it a Content of ":"
Use the Designer or edit the xaml to adjust the various heights
and widths so that your control looks nicely arranged, for example
like this:

We will not allow the user to type values into the
HourMinutePicker. This avoids some difficulties, notably the fact
that if we try and validate as the user enters text then we don't
know when they entered '4' whether that's a valid reference to 4
am, or 4 pm, or whether they are about to type another digit such
as '2' to create an invalid hour ('42'). Our control is relatively
unsophisticated so we only allow them to change values by clicking
on "spinner" arrows within the control. So disable the user input
in the hour and minute text boxes by setting their "readonly" flags
to "true" .
Add the "uparrow" and "downarrow" images to the solution.


Position them in the user control to produce spinners:

Add a Property
Our user control needs a property to represent the hour and
minute, so we will create a DateTime property named HourMinute.
The User Control as a Binding Target
We want our user control to be a binding target. This means that
we can bind another object to it and the framework will take care
of updating that data object with the values entered in the control
by the user. Similarly our user control will reflect the value of
the data object, so for example when the control is opened it
displays the hour and minute from the bound object.
We are going to bind our control to a DateTime member in an
instance of the class "Task". In the data binding scenario this
means that our user control is a binding target and therefore its
property to be bound must be a "DependencyProperty". The process of
defining dependency properties using the HourMinutePicker as an
example is described here:
/blog/2011/9/20/wpf-user-controls-turn-your-user-control-into-a-data-binding-target/
The User Control as a Binding Source
Our user control also needs to act as a binding source. This is
because we want to be able to bind our hour and minute text boxes
as binding targets to the hour and minute contained within our
HourMinute property. This means our user control's HourMinute
property is the data source in the binding scenario. To support
data binding source behaviour the user control must implement the
INotifyPropertyChanged interface. That means updates to the
HourMinute property can be notified to the appropriate text
controls.
public partial class HourMinutePicker : UserControl, INotifyPropertyChanged
We then implement the INotifyPropertyChanged
interface:
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
The HourMinute property must raise the change notification:
OnPropertyChanged("HourMinute");
The user control must set up the binding between its HourMinute
property and the text boxes. This can be done in xaml or in the C#.
Here it is in C#, called in the constructor of the user
control:
var hourBinding = new Binding();
hourBinding.Source = this;
hourBinding.Path = new PropertyPath("HourMinute.Hour");
hourBinding.Mode = BindingMode.OneWay;
textBoxHour.SetBinding(TextBox.TextProperty, hourBinding);
var minuteBinding = new Binding();
minuteBinding.Source = this;
minuteBinding.Path = new PropertyPath("HourMinute.Minute");
minuteBinding.Mode = BindingMode.OneWay;
textBoxMinute.SetBinding(TextBox.TextProperty, minuteBinding);
The binding is one-way because we just want the text boxes to
reflect the value of the property (the value itself is controlled
via the spinners).
The User Control as Both Target and Source
Combining the changes required for supporting the binding target
and binding source behaviours results in these property
definitions:
public static DependencyProperty HourMinuteProperty = DependencyProperty.Register(
"HourMinute", typeof(DateTime), typeof(HourMinutePicker),
new PropertyMetadata(OnHourMinChanged));
private DateTime _hourMinute = new DateTime(1900, 1, 1);
static void OnHourMinChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj != null && obj is HourMinutePicker)
{
(obj as HourMinutePicker).UpdateTime(e);
}
}
private void UpdateTime(DependencyPropertyChangedEventArgs e)
{
_hourMinute = (DateTime) e.NewValue;
}
public DateTime HourMinute
{
get
{
return (DateTime) GetValue(HourMinuteProperty);
}
set
{
SetValue(HourMinuteProperty, value);
OnPropertyChanged("HourMinute");
}
}
Implement Command Handling
Now we need to capture user mouse clicks on the up and down
arrows. We will use routed commands, so in the code for the user
control (HourMinutePicker.xaml.cs) we declare 4 static routed
commands:
public static RoutedCommand HourUpTask = new RoutedCommand();
public static RoutedCommand MinuteUpTask = new RoutedCommand();
public static RoutedCommand HourDownTask = new RoutedCommand();
public static RoutedCommand MinuteDownTask = new RoutedCommand();
We can associate these commands with the spinner images (up and down arrows) in the user control's xaml:
<Image …>
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{x:Static Controls:HourMinutePicker.HourDownTask}" />
</Image.InputBindings>
</Image>
Now we can bind to these commands in the constructor of our user control:
this.CommandBindings.Add(new CommandBinding(HourUpTask, ExecuteHourDownTask, CanExecuteHourMinute));
this.CommandBindings.Add(new CommandBinding(MinuteUpTask, ExecuteMinuteDownTask, CanExecuteMinuteTask));
this.CommandBindings.Add(new CommandBinding(HourDownTask, ExecuteHourUpTask,CanExecuteHourMinute));
this.CommandBindings.Add(new CommandBinding(MinuteDownTask, ExecuteMinuteUpTask, CanExecuteMinuteTask));
Note that each command references two methods: an execute method
and a "can execute" method. The "can execute" method is called to
determine whether the command processing should proceed for this
handler. For our user control we do not need to validate so we can
always return true. This is because an advantage of using our
spinner inputs with a DateTime property is that the DateTime class
will automatically roll the values round the hour and minute
boundaries as we add or subtract values. The user cannot
enter an invalid character. Therefore we just need a single "can
execute" command that returns true. We need four execute
methods:
public void ExecuteHourDownTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddHours(-1);
}
public void ExecuteHourUpTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddHours(1);
}
public void ExecuteMinuteDownTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddMinutes(-1);
}
public void ExecuteMinuteUpTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddMinutes(1);
}
public void CanExecuteHourMinute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
The "execute" tasks are self-explanatory, they simply amend the
appropriate hour or minute field in the required direction.
Using the User Control
The user control can now be used. We will embed it in the xaml
for a dialog that updates the properties of a Task object. Our user
control manages the hour and minute for the scheduling details of
the task:
<Controls:HourMinutePicker HourMinute="{Binding Task.ScheduledHourMin, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
Note that we have declared our control and bound it to the
"ScheduledHourMin" property of our Task object. You can set the
attributes of the control such as its border thickness and
colour.
Complete Source Code
xaml
<UserControl x:Class="FtpSync.Controls.HourMinutePicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Controls="clr-namespace:FtpSync.Controls" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="HMPicker"
>
<Border Height="29" Width="73" BorderThickness="1" BorderBrush="Black">
<Grid Height="19" Width="66">
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="9*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="11*"/>
<ColumnDefinition Width="14*"/>
<ColumnDefinition Width="65*" />
</Grid.ColumnDefinitions>
<Label Content=":" Height="25" HorizontalAlignment="Left" Margin="8.5,-5,0,0" Name="label1" VerticalAlignment="Top" DataContext="{Binding}" Width="Auto" Grid.Column="2" d:LayoutOverrides="HorizontalAlignment" Grid.RowSpan="2" />
<TextBox Height="18" Name="textBoxMinute" VerticalAlignment="Top" Grid.Column="2" BorderThickness="0" Grid.RowSpan="2" Margin="19.001,1,-0.333,0" />
<TextBox Height="19" Name="textBoxHour" VerticalAlignment="Top" IsEnabled="True" IsReadOnly="True" Grid.ColumnSpan="2" BorderThickness="0" Grid.RowSpan="2" Margin="0,1,-10.667,0" d:LayoutOverrides="GridBox" />
<Image HorizontalAlignment="Left" Height="4" Margin="3.5,1,0,0" Source="/FtpSync;component/Images/downarrow.png" Stretch="Fill" VerticalAlignment="Top" Width="7" Grid.Column="2">
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{x:Static Controls:HourMinutePicker.HourUpTask}" />
</Image.InputBindings>
</Image>
<Image HorizontalAlignment="Left" Height="4" Margin="3.5,5,0,0" Source="/FtpSync;component/Images/uparrow.png" Stretch="Fill" VerticalAlignment="Top" Width="7" Grid.Column="2" Grid.Row="1">
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{x:Static Controls:HourMinutePicker.HourDownTask}" />
</Image.InputBindings>
</Image>
<Image HorizontalAlignment="Left" Margin="41.5,1,0,0" Source="/FtpSync;component/Images/downarrow.png" Stretch="Fill" Width="7" Grid.Column="2" Height="4" VerticalAlignment="Top">
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{x:Static Controls:HourMinutePicker.MinuteUpTask}" />
</Image.InputBindings>
</Image>
<Image HorizontalAlignment="Left" Height="4" Margin="41,5,0,0" Source="/FtpSync;component/Images/uparrow.png" Stretch="Fill" VerticalAlignment="Top" Width="7" Grid.Column="2" Grid.Row="1">
<Image.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{x:Static Controls:HourMinutePicker.MinuteDownTask}" />
</Image.InputBindings>
</Image>
</Grid>
</Border>
</UserControl>
C#
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace FtpSync.Controls
{
public partial class HourMinutePicker : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public static RoutedCommand HourUpTask = new RoutedCommand();
public static RoutedCommand MinuteUpTask = new RoutedCommand();
public static RoutedCommand HourDownTask = new RoutedCommand();
public static RoutedCommand MinuteDownTask = new RoutedCommand();
public static DependencyProperty HourMinuteProperty = DependencyProperty.Register(
"HourMinute", typeof(DateTime), typeof(HourMinutePicker),
new PropertyMetadata(OnHourMinChanged));
private DateTime _hourMinute = new DateTime(1900, 1, 1);
static void OnHourMinChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj != null && obj is HourMinutePicker)
{
(obj as HourMinutePicker).UpdateTime(e);
}
}
private void UpdateTime(DependencyPropertyChangedEventArgs e)
{
_hourMinute = (DateTime) e.NewValue;
}
public DateTime HourMinute
{
get
{
return (DateTime) GetValue(HourMinuteProperty);
}
set
{
SetValue(HourMinuteProperty, value);
OnPropertyChanged("HourMinute");
}
}
public HourMinutePicker()
{
InitializeComponent();
this.CommandBindings.Add(new CommandBinding(HourUpTask, ExecuteHourDownTask,
CanExecuteHourMinute));
this.CommandBindings.Add(new CommandBinding(MinuteUpTask, ExecuteMinuteDownTask,
CanExecuteMinuteTask));
this.CommandBindings.Add(new CommandBinding(HourDownTask, ExecuteHourUpTask,
CanExecuteHourMinute));
this.CommandBindings.Add(new CommandBinding(MinuteDownTask, ExecuteMinuteUpTask,
CanExecuteMinuteTask));
var hourBinding = new Binding();
hourBinding.Source = this;
hourBinding.Path = new PropertyPath("HourMinute.Hour");
hourBinding.Mode = BindingMode.OneWay;
textBoxHour.SetBinding(TextBox.TextProperty, hourBinding);
var minuteBinding = new Binding();
minuteBinding.Source = this;
minuteBinding.Path = new PropertyPath("HourMinute.Minute");
minuteBinding.Mode = BindingMode.OneWay;
textBoxMinute.SetBinding(TextBox.TextProperty, minuteBinding);
}
public void CanExecuteHourMinute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public void ExecuteHourDownTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddHours(-1);
}
public void ExecuteHourUpTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddHours(1);
}
public void ExecuteMinuteDownTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddMinutes(-1);
}
public void ExecuteMinuteUpTask(object sender, ExecutedRoutedEventArgs e)
{
HourMinute = _hourMinute.AddMinutes(1);
}
public void CanExecuteMinuteTask(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (_hourMinute.Minute >= 0 && _hourMinute.Minute <= 59);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}