1

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)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Berryl
  • 12,471
  • 22
  • 98
  • 182

2 Answers2

4

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'
Steve Greatrex
  • 15,789
  • 5
  • 59
  • 73
  • Yeah this is what I think should be the fix. I am having a problem which I made as a [separate question here](http://stackoverflow.com/questions/10639274/datatrigger-error). Can you have a look at that one? – Berryl May 17 '12 at 16:04
  • I suppose there is no way round repeating the conditions for each Gender value even though it will always be Img_Male unless it actually is a Female, yes? Cheers – Berryl May 17 '12 at 16:05
1

As an option you can use custom converter class and convert viewmodel to bitmap source. If you wish to use triggers, then you can use some multidatatriggers and/or multiconverters for example for cases when you want to show Img_Male.

But these solutions isn't really good I think, better to introduce property/logic and simply bind image source to it and handle these view logic inside viewmodel. Using this approach you can write unit tests for this logic also.

trimeyko
  • 129
  • 6
  • 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. Cheers – Berryl May 17 '12 at 13:20
  • Unit testing images is problematic also, since image equality not provided (understandably so). Cheers – Berryl May 17 '12 at 13:22
  • It is still application logic to choose between some images and show correct one, viewmodel is created to accumulate all view logic / properties inside and you can easily code up new views without copy-pasting some xaml triggers etc. You can still unit test path property to bitmap in different logic cases. Why you so afraid to write these logic in VM ? Btw don't clearly understand sentence about converters, but multiconverters just output one value based on multi value input but dont convert type. – trimeyko May 17 '12 at 13:27
  • See [this question](http://stackoverflow.com/questions/10638201/multiconverter-usage) which you might have some answers to that would also apply to a vm sort of solution. Cheers – Berryl May 17 '12 at 15:00
  • I don't have enough rep to comment on your second question, so i will write it here : multivalueconverters input and output values must be all same type, this is the reason why you receive UnsetValue in your example – trimeyko May 17 '12 at 15:15
  • If you want to take a shot at my other post looking for a converter solution it might be an easy way to start building up your rep! Cheers – Berryl May 17 '12 at 16:08