Click here to Skip to main content
15,886,873 members
Articles / Desktop Programming / WPF

WPF Templates – The Event Aspect

Rate me:
Please Sign up or sign in to vote.
4.69/5 (26 votes)
2 Apr 2014CPOL3 min read 26.6K   577   50   4
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:

HTML
<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:

C#
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:

Image 1

Now, let's see the event behavior of Data-Template:

HTML
<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:

Image 2

And the Data-Template represents some class:

C#
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:

Image 3

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:

Image 4

User-Control

The simplest way to do it is to take matters into your hand, i.e. use a User-Control:

HTML
<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:

HTML
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:

Image 5

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:

HTML
<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:

C#
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:

C#
/// <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:

HTML
 <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:

Image 6

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer GalilCS
Israel Israel
My name is Shai Vashdi and I’m the CTO & Co-Founder of GalilCS.
GalilCS provides industrialized, low-cost IT services (ILCS) to corporations around the globe. We specialize in code modernization projects (VB6 to C# WinForms/HTML5, for example) and code refactoring projects.
Most of my work revolves around the management of these projects and the creation of new tools for refactoring code. As a bonus, I also get to lecture about Microsoft programming languages.
For more information, visit GalilCS.

Comments and Discussions

 
GeneralMy vote of 2 Pin
Irina Pykhova12-May-14 10:23
professionalIrina Pykhova12-May-14 10:23 
I wrote here a reason, but author found me on LinkedIn and gave an hour to remove my comments.
Did my best in removing

modified 12-May-14 22:19pm.

Suggestionplease, work a bit more on this article Pin
Irina Pykhova12-May-14 10:18
professionalIrina Pykhova12-May-14 10:18 
GeneralRe: please, work a bit more on this article Pin
Shai Vashdi12-May-14 21:55
Shai Vashdi12-May-14 21:55 
Questionvery nice Pin
BillW339-May-14 7:11
professionalBillW339-May-14 7:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.