WPF Datatrigger not firing when expected
Asked Answered
P

2

31

I have the following XAML:

<TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Margin="0,0,5,0"/>
<TextBlock Text="items selected">
    <TextBlock.Style>
        <Style TargetType="{x:Type TextBlock}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Value="1">
                    <Setter Property="TextBlock.Text" Value="item selected"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

The first text block happily changes with SelectedItems.Count, showing 0,1,2, etc. The datatrigger on the second block never seems to fire to change the text.

Any thoughts?

Pyrophoric answered 18/9, 2008 at 16:29 Comment(4)
You know, I have no idea why this keeps getting downvotes - it's a legitimate question. If you downvote, at least leave a comment why.Pyrophoric
That is bizarre, sending an upvote your wayCushman
It might be the vague title, but this is a common problem with DataTriggers and this is a good example, +1Vickeyvicki
Thanks, I tried to update the title to be less vague.Pyrophoric
A
13

The DataTrigger is firing but the Text field for your second TextBlock is hard-coded as "items selected" so it won't be able to change. To see it firing, you can remove Text="items selected".

Your problem is a good candidate for using a ValueConverter instead of DataTrigger. Here's how to create and use the ValueConverter to get it to set the Text to what you want.

Create this ValueConverter:

public class CountToSelectedTextConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((int)value == 1)
            return "item selected";
        else
            return "items selected";
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Add the namespace reference to your the assembly the converter is located:

xmlns:local="clr-namespace:ValueConverterExample"

Add the converter to your resources:

<Window.Resources>
    <local:CountToSelectedTextConverter x:Key="CountToSelectedTextConverter"/>
</Window.Resources>

Change your second textblock to:

    <TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count, Converter={StaticResource CountToSelectedTextConverter}}"/>
Athelstan answered 18/9, 2008 at 17:29 Comment(2)
You're right, removing the Text worked. I'm just going to go with item(s) selected for text since I don't feel like creating an entire converter for this. However, I'm marking this as an accepted answer because it would work.Pyrophoric
What about if you needed the datatrigger to control a storyboard? A converter is not appropriate in that scenario. Any advice?Emmi
V
29

Alternatively, you could replace your XAML with this:

<TextBlock Margin="0,0,5,0" Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count}"/>
<TextBlock>
    <TextBlock.Style>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Text" Value="items selected"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=EditListBox, Path=SelectedItems.Count}" Value="1">
                    <Setter Property="Text" Value="item selected"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

Converters can solve a lot of binding problems but having a lot of specialized converters gets very messy.

Vickeyvicki answered 19/9, 2008 at 21:0 Comment(1)
Thanks, a simple solution to a very stupid restriction/design flaw/Micro$oft imposed irritation.Term
A
13

The DataTrigger is firing but the Text field for your second TextBlock is hard-coded as "items selected" so it won't be able to change. To see it firing, you can remove Text="items selected".

Your problem is a good candidate for using a ValueConverter instead of DataTrigger. Here's how to create and use the ValueConverter to get it to set the Text to what you want.

Create this ValueConverter:

public class CountToSelectedTextConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((int)value == 1)
            return "item selected";
        else
            return "items selected";
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Add the namespace reference to your the assembly the converter is located:

xmlns:local="clr-namespace:ValueConverterExample"

Add the converter to your resources:

<Window.Resources>
    <local:CountToSelectedTextConverter x:Key="CountToSelectedTextConverter"/>
</Window.Resources>

Change your second textblock to:

    <TextBlock Text="{Binding ElementName=EditListBox, Path=SelectedItems.Count, Converter={StaticResource CountToSelectedTextConverter}}"/>
Athelstan answered 18/9, 2008 at 17:29 Comment(2)
You're right, removing the Text worked. I'm just going to go with item(s) selected for text since I don't feel like creating an entire converter for this. However, I'm marking this as an accepted answer because it would work.Pyrophoric
What about if you needed the datatrigger to control a storyboard? A converter is not appropriate in that scenario. Any advice?Emmi

© 2022 - 2024 — McMap. All rights reserved.