Search code examples
c#xaml

Show XAML on Condition


The context is I have a Listbox that is outputting items with quite a lot of styling (more than i've put here)... I went to use a DataTemplateSelector to show either one image or another based on a condition but it was stupid to have to re-write the whole tempalte with just this one difference in it.

I have placed some pseudo code to demonstrate what i'm trying to achieve:

<ListBox Grid.Row="1"
            ItemsSource="{Binding TestCases}"
            BorderThickness="0"
            HorizontalAlignment="Stretch"
            HorizontalContentAlignment="Stretch">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="auto"></ColumnDefinition>
                    <ColumnDefinition Width="10"></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Column="0"
                            Padding="0,5,0,5">
                    <Run Text="{Binding Class}"></Run>
                </TextBlock>

                <TextBlock Grid.Column="1"
                            Padding="5">
                    <Run Text="{Binding Assertions}"></Run>
                </TextBlock>

if {Binding Failures} > 0 {

    <Image HorizontalAlignment="Center"
            Width="10"
            Height="10"
            Grid.Column="2"
            Source="/Content/Images/Cross.png">
    </Image>

}
else
{

    <Image HorizontalAlignment="Center"
            Width="10"
            Height="10"
            Grid.Column="2"
            Source="/Content/Images/Tick.png">
    </Image>

}

            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Any ideas on how i do it?

-------------Edit------------

So far i've added the namespace of my converter class to the XAML file:

xmlns:converters="clr-namespace:Blah.Blah.Converters"

Then added window resources:

<Window.Resources>
    <converters:FailuresTickConverter x:Key="failuresTickConverter" />
    <converters:FailuresCrossConverter x:Key="failuresCrossConverter" />
</Window.Resources>

Then i have the classes themselves:

namespace Blah.Blah.Converters
{
    public class FailuresTickConverter : IValueConverter
    {
        public object Convert( object value, Type targetType,
            object parameter, CultureInfo culture )
        {
            int failures = (int)value;

            if( failures > 0 )
                return Visibility.Hidden;
            return Visibility.Visible;
        }

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

    public class FailuresCrossConverter : IValueConverter
    {
        public object Convert( object value, Type targetType,
            object parameter, CultureInfo culture )
        {
            int failures = ( int )value;

            if( failures > 0 )
                return Visibility.Visible;
            return Visibility.Hidden;
        }

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

Then in my XAML on the images i've done this:

<Image Visibility="{Binding Failures, Converter=failuresTickConverter}"
        HorizontalAlignment="Center"
        Width="10"
        Height="10"
        Grid.Column="2"
        Grid.Row="0"
        Source="/Content/Images/Tick.png">
</Image>

<Image Visibility="{Binding Failures, Converter=failuresCrossConverter}"
        HorizontalAlignment="Center"
        Width="10"
        Height="10"
        Grid.Column="2"
        Grid.Row="0"
        Source="/Content/Images/Cross.png">
</Image>

I'm getting an error on the bindings:

IValueConverter does not support converting from string


Solution

  • Elaborating on my comment, here's a simple (not the only one, of course) way to do it:

    Create a converter to convert number to visibility:

    public class PositiveToVisibilityConverter: IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return (int)value > 0 ? Visibility.Visible : Visibility.Collapsed;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    Add the namespace of the converter to your xaml, add the converter to resources (use your own namespace of course):

    <Window ...
            xmlns:converters="clr-namespace:WpfApplication1.Converters"
            ...>
        <Window.Resources>
            <converters:PositiveToVisibilityConverter x:Key="PositiveConverter"/>
        </Window.Resources>
    

    Then put both the images in, the "success" one first and bind the property using the converter to the "failure" one:

    <Image HorizontalAlignment="Center"
            Width="10"
            Height="10"
            Grid.Column="2"
            Source="/Content/Images/Tick.png"
            Visibility="{Binding Failures, Converter={StaticResource PositiveConverter}}">
    </Image>
    <Image HorizontalAlignment="Center"
            Width="10"
            Height="10"
            Grid.Column="2"
            Source="/Content/Images/Cross.png">
    </Image>
    

    Since these images are in a grid (I assume) and have the same position and size, they will overlap and normally the one that's defined last will be drawn last, so the first one will not be visible. That is, unless the binding makes the second image invisible.