Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Templates – The Event Aspect

0.00/5 (No votes)
2 Apr 2014 1  
WPF templates, what is determining the events behavior?

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here