Home > View Post

Help! Why can't I use DataTriggers with controls in WPF?

or "Why can't I use TargetName with Style's Triggers"?

This is a common problem. The first is the desire to use DataTriggers directly with an element. Something like this slightly contrived example:

<CheckBox Name="chk" Margin="5" Content="Check me" IsChecked="True"/>
<Border Margin="5" Background="Navy" CornerRadius="5" Padding="5">
<TextBlock Foreground="White">I should disappear when you uncheck the textbox</TextBlock>
    <Border.Triggers>
        <DataTrigger Binding="{Binding ElementName=chk, Path=IsChecked}" Value="False">
            <Setter Property="Border.Visibility" Value="Hidden"/>
            <Setter Property="Border.Visibility" Value="Hidden"/>
        </DataTrigger>
    </Border.Triggers>
</Border>

Here, we have a checkbox next to a border containing some text. We also have a DataTrigger registered to change the visibility of the border when the checkbox is unchecked.

The first problem with this is that it's totally invalid. Elements (like Border) can only contain EventTriggers (which actually, would be quite suitable for this example but bare with me).

Only Styles, DataTemplates and ControlTemplates support the awesome DataTrigger, so it's time for a bit of a cheat.

Using a style

<CheckBox Name="chk" Margin="5" Content="Check me" IsChecked="True"/>
<Border Margin="5" Background="Navy" CornerRadius="5" Padding="5">
    <TextBlock Foreground="White">I should disappear when you uncheck the textbox</TextBlock>
    <Border.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=chk, Path=IsChecked}" Value="False">
                    <Setter Property="Border.Visibility" Value="Hidden"/>
                    <Setter Property="Border.Visibility" Value="Hidden"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Border.Style>
</Border>

It's perhaps a bit verbose but it compiles (and works!). Happy days.

However, what if we want to modify a property of the TextBlock in our trigger, not just the border? Problem #2 - I can only change the properties of the element to which the style can apply.

Using a DataTemplate

Time to escalate our cheating slightly, time for a DataTemplate. The great thing about DataTemplates is they allow DataTriggers and support the TargetName property on their Setters.

To implement this, we just use what I call the ContentPresenter/DataTemplate combo, with this lot in resources:

<DataTemplate x:Key="myDataTemplate">
    <Border Name="border" Margin="5" Background="Navy" CornerRadius="5" Padding="5">
        <TextBlock Name="text" Foreground="White">I should fade and go red when you uncheck the textbox</TextBlock>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding ElementName=chk2, Path=IsChecked}" Value="False">
            <Setter TargetName="border" Property="Border.Opacity" Value=".5"/>
            <Setter TargetName="text" Property="TextBlock.Foreground" Value="Red"/>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

and a ContentPresenter wherever you want to use the template:

<CheckBox Name="chk2" Margin="5" Content="Check me" IsChecked="True"/>
<ContentPresenter ContentTemplate="{StaticResource myDataTemplate}"/>

Another great thing is this template is now reusable elsewhere within the scope of the resources - just specify another ContentPresenter.

I use this approach all the time as it helps to create a more readable structure within my Xaml files - in addition to giving me support for DataTriggers and TargetName.

Tags: WPF

 
Josh Post By Josh Twist
11:43 PM
15 Jun 2009

» Next Post: Merging XPS Documents
« Previous Post: GetHashCode and LINQ to objects

Comments are closed for this post.

Posted by Ben Lowe @ 16 Jun 2009 1:14 AM
Hi josh, We're using DataTemplates in this way in WPF and Silverlight. Unfortunately there's a bug with Silverlight and WPF's integration with UIA in that those TextBlocks (or anything that contains a TextBlock in its template, e.g. Label) don't show up in the Control or Content View when used inside a DataTemplate. This means that screen readers that use the UIA can miss out fair portions of the content. Got a bug outstanding at the moment on this so hopefully someone will take ownership of sorting it out.

Posted by josh @ 16 Jun 2009 1:26 AM
Cheers Ben,

Useful to know.

Posted by Ali @ 23 Feb 2011 11:16 AM
Thanks - was very useful

Posted by Caleb Johnson @ 18 May 2011 3:50 PM
I have a question. When looking at this line:
<DataTrigger Binding="{Binding ElementName=chk2, Path=IsChecked}" Value="False"> I am doing something similar except I am binding to a bool property that is located in my code behind. How would I bind to that?

Posted by Josh @ 18 May 2011 6:43 PM
Hi Caleb,

You set the DataContext of the Control (window probably in this case) to be the class that has the property - the windows :)

I'd probably recommend you move away from having this in the code-behind and create a ViewModel proper. There are plenty of MVVM tutorials out there on the web.

HTH

Josh

© 2005 - 2017 Josh Twist - All Rights Reserved.