wpfxamldata-bindingdatatrigger

DataTrigger Usage


Here is pseudo code for what I want to implement in xaml:

IF vm.AvatarFilePath IS NOT NULL THEN
    Image.Source = {Binding AvatarPath}
ELSE
    If vm.Gender == {x:Static vm:Gender.Female} THEN
        Image.Source = {StaticResource Img_Female}
    ELSE
        Image.Source = {StaticResource Img_Male}
    ENDIF
ENDIF

and below is an implementation attempt with at least the following issues:

  1. How does it know the AvatarPath was null and that we care about Gender?
  2. Is there a way to do ELSE, so I can specify the Gender.Male resource only once instead of once each for

How can I implement this properly?

Cheers,
Berryl

XAML attempt #1

<DataTemplate x:Key="AvatarPathTemplate">
    <Image x:Name="avatarImage" Source="{Binding AvatarPath}"/>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Gender}" Value="{x:Static vm:Gender.Female}">
            <Setter Property="Source" Value="{resx:Resx Img_Female}"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Gender}" Value="{x:Static vm:Gender.Male}">
            <Setter Property="Source" Value="{resx:Resx Img_Male}"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Gender}" Value="{x:Static vm:Gender.Unknown}">
            <Setter Property="Source" Value="{resx:Resx Img_Male}"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Gender}" Value="{x:Static vm:Gender.Unspecified}">
            <Setter Property="Source" Value="{resx:Resx Img_Male}"/>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Update: as trimeyko points out, this could be done either with a multiconverter or inside of a view model.

Per my answer back: "I actually tried the multiconverter approach at first, with modest success, and almost posted that to help clean it up. Then I decided that converters are best left to actually converting types. Agreed the view model approach is probably easiest but this does seem more to be the view's job, and I'd like to see if I can get it to work as such first."

I made my attempt at [solving this with a mutliConveter posting here] (https://stackoverflow.com/questions/10638201/multiconverter-usage)


Solution

  • You should be able to do this with a couple of MultiDataTriggers:

    <DataTemplate x:Key="AvatarPathTemplate">
        <Image x:Name="avatarImage" Source="{Binding AvatarPath}"/>
        <DataTemplate.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding AvatarPath}" Value="{x:Null}" />
                    <Condition Binding="{Binding Gender}" Value="{x:Static vm:Gender.Female}"/>
                </MultiDataTrigger.Conditions>
                <Setter Property="Source" Value="{resx:Resx Img_Female}"/>
            </MultiDataTrigger>
    
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding AvatarPath}" Value="{x:Null}" />
                    <Condition Binding="{Binding Gender}" Value="{x:Static vm:Gender.Male}"/>
                </MultiDataTrigger.Conditions>
                <Setter Property="Source" Value="{resx:Resx Img_Male}"/>
            </MultiDataTrigger>
            <!-- etc... -->
        </DataTemplate.Triggers>
    </DataTemplate>
    

    These are stating 'when AvatarPath is null AND Gender is female...'

    Further Improvement

    As DataTriggers are applied in the order in which they appear in the XAML, we can remove the need for duplication of the 'Male' settings in the example with the below:

    <DataTemplate x:Key="AvatarPathTemplate">
        <Image x:Name="avatarImage" Source="{Binding AvatarPath}"/>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding AvatarPath}" Value="{x:Null}">
                <Setter Property="Source" Value="{resx:Resx Img_Male}"/>
            </DataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding AvatarPath}" Value="{x:Null}" />
                    <Condition Binding="{Binding Gender}" Value="{x:Static vm:Gender.Female}"/>
                </MultiDataTrigger.Conditions>
                <Setter Property="Source" Value="{resx:Resx Img_Female}"/>
            </MultiDataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
    

    Here we are saying:

    1. Set the source to AvatarPath
    2. If AvatarPath is null, set the source to 'Img_Male'
    3. If the AvatarPath is null AND the Gender is female, set the source to 'Img_Female'