WPF Templates – The Event Aspect






4.69/5 (25 votes)
WPF templates, what is determining the events behavior?
- Download ControlTemplateVsDataTemplate (no EXE) - 33.2 KB
- Download ControlTemplateVsDataTemplate - 69.5 KB
- Download sample code (no EXE) - 40.2 KB
- Download sample code - 120.6 KB
Introduction
WPF enables us to override visualization of controls using templates, looking on the event aspect of these templates, what is determining the events behavior?
Background
WPF enables us various ways to override the visualization of controls, we can override the Control-Template, Data-Template or create a custom user control combining these two...
Most articles I have encountered so far, dealt in detail with the UI aspect of the templates, but less attention was given to the way in which events are used. In the following lines, I want to discuss the difference in the event logic between Control-Template to Data-Template.
Control-Template Vs Data-Template
While Control-Templates handle events using its logical tree representation, Data-Templates handle events using its visual tree representation.
For example, Control-Template:
<CheckBox Grid.Column="0" Grid.Row="1" x:Name="checkbox"
Unchecked="CheckBox_Unchecked" Checked="CheckBox_Checked">
<CheckBox.Template>
<ControlTemplate TargetType="CheckBox">
<StackPanel Orientation="Vertical">
<Border Margin="5" BorderThickness="1" BorderBrush="Black"
Width="20" Height="20" Background="Black"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
The code behind is as follows:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
MessageBox.Show(checkbox.IsChecked.ToString());
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
MessageBox.Show(checkbox.IsChecked.ToString());
}
As
we can see, although the template is a Border
inside a StackPanel
, the control event
behavior is of Check-Box:

Now, let's see the event behavior of Data-Template:
<ListBox Grid.Column="1" Grid.Row="1"
ItemsSource="{Binding Items}" Height="129" HorizontalAlignment="Left" Name="listBox1"
VerticalAlignment="Top" Width="145" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding Path=Name}" />
<Button Margin="2" Width="40" Command="{Binding Path=Command}"
CommandParameter="{Binding Path=CommandParam}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
We
can see a simple ListBox
with Data-Template of TextBlock
+ Button
:
And the Data-Template represents some class:
public class Class1
{
public string Name { get; set; }
public ICommand Command { get; set; }
public object CommandParam { get; set; }
}
Clicking
on the button in the ListBox
gives us the button event:
Here lies the big difference: while overriding Checkbox
, the Control-Template keeps the
Check-Box event behavior, overriding the ListBox
Data-Template (i.e., the
template of the ListBoxItem
) makes the event behavior of the ListBoxItem
to be
as the Data-Template and not the ListBoxItem
! That is to say, the events are now triggered by the
Visual-Tree and not the Logical tree!
Of course, this is somewhat confusing… one template overrides the events (Data-Template) and one template does not (Control-Template).
But
this is not the only difference; while Control-Template overrides the
visualization of a control while Data-Template overrides the visualization of a list
of objects inside an ItemsControl
.
So what can we do if we want to override the visualization of a control & the event behavior? For example, creating a button with two visual circles but only one gets the click event:
User-Control
The simplest way to do it is to take matters into your hand, i.e. use a User-Control:
<UserControl x:Class="WpfApplication9.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="100">
<Grid>
<WrapPanel Orientation="Horizontal">
<Ellipse Fill="Black" Width="50" Height="50" MouseDown="Ellipse_MouseDown"
/>
<Ellipse Fill="Black" Width="50" Height="50"/>
</WrapPanel>
</Grid>
</UserControl>
The code behind is as follows:
private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Click");
}
Here, you can control the behavior of your events using the controls you like, the Control-Templates you like and even the Data-Templates you like.
Summary
To sum things up, let's see how to connect events to UI elements in 3 different ways:
UI: |
Control-Template |
Data-Template |
User-Control |
Event logic: |
By Logical Tree |
By Visual Tree |
By your logic |
Overrides visualization of: |
Control |
List of objects inside an ItemsControl. |
By your logic |
Farther Discussion
For the sake of discussion, let's try to understand the difference in another way:
What if there was not such a thing as Data-Template? Let's try to implement the Data-Template example from above using Control-Template of a custom list-box:
First, let's create a User-Control containing a ListBox
:
<UserControl x:Class="ListBoxWithItemsControlTemplate.CustomListBox"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ListBox x:Name="listbox"/>
</Grid>
</UserControl>
The User-Control can get a list of items (Class1
items for example), for each Item we are creating a ListBoxItem with Control-Template and add it to the inner ListBox
:
public void UpdateItems(IEnumerable items)
{
foreach (var item in items)
{
//Default Content is item.ToString():
var lbItem = new ListBoxItem() { Content = item.ToString()};
if (ListBoxItemTemplate != null)
lbItem.Template = ListBoxItemTemplate;
this.listbox.Items.Add(lbItem);
}
}
We also need to expose the ItemsSource
&
ListBoxItemTemplate
as Dependency Properties so we can use them at the
MainWindow
XAML:
/// <summary>
/// Interaction logic for CutomListBox.xaml
/// </summary>
public partial class CustomListBox : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CustomListBox), new
FrameworkPropertyMetadata(null, ItemsSourcePropertyChanged));
public static readonly DependencyProperty ListBoxItemTemplateProperty =
DependencyProperty.Register("ListBoxItemTemplate", typeof(ControlTemplate), typeof(CustomListBox), new
FrameworkPropertyMetadata(null));
public IEnumerable ItemsSource
{
get
{
return (IEnumerable)base.GetValue(ItemsSourceProperty);
}
set
{
base.SetValue(ItemsSourceProperty, value);
UpdateItems(value);
}
}
private static void ItemsSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var listbox = d as CustomListBox;
if (listbox != null)
{
listbox.ItemsSource = e.NewValue as IEnumerable;
}
}
public ControlTemplate ListBoxItemTemplate
{
get
{
return (ControlTemplate)base.GetValue(ListBoxItemTemplateProperty);
}
set
{
base.SetValue(ListBoxItemTemplateProperty, value);
}
}
public CustomListBox()
{
InitializeComponent();
}
public void UpdateItems(IEnumerable items)
{
...
}
}
Let's look at the control on the MainWindow
XAML:
<my:CustomListBox Width="140"
ItemsSource="{Binding Items}" x:Name="customListBox1">
<my:CustomListBox.ListBoxItemTemplate>
<ControlTemplate TargetType="ListBoxItem">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Width="40"
Background="AliceBlue" Text="{TemplateBinding Content}" />
<Button Margin="2" Width="40"
Command="{TemplateBinding Tag}"
CommandParameter="{TemplateBinding Content}" />
</StackPanel>
</ControlTemplate>
</my:CustomListBox.ListBoxItemTemplate>
</my:CustomListBox>
We
are binding the ItemSource
to some list of Class1
objects, and creating a Control-Template
for each ListBoxItem
in the list.
Please
Notice: To simplify the example, we store the item's command in the
ListBoxItem
tag.
Now, let's notice the change comparing to Data-Template:
Each
click on the Template button raises a "MouseDown
" event at the ListBoxItem
& Template Button command:
When we've used the Data-Template, only the Template Button Command was raised.
Please Notice: In order to fully understand the small syntax nuance, please download the code sample from the links at the top of this article.