Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Binding enum to Visibility

Tags:

c#

binding

wpf

xaml

I am using the MVVM pattern (Getting Started) For WPF application

I Have a property called TrainDirection defined in the classTrain.cs of the model :

public enum TrainDirection
{
    Unknown,
    None,
    Left,
    Right
}

in the View I want to Show/Hide a symbol that represents the trainObject according to the value of the enum.

enter image description here

I created a 'UserControl' :

<Grid >
    <Path x:Name="TrainToRight" Data="M80,160L220,160 270,190 220,220 80,220"  Stretch="Fill"  StrokeThickness="2" Opacity="0.9"      
    </Path>

    <Path x:Name="TrainToLeft" Data="M130,160L260,160 260,220 130,220 80,190z" Stretch="Fill"  StrokeThickness="2" Opacity="0.9"
    </Path>        
</Grid>

I guess I need something like a trainDirectionToVisibilityConverter to bind the visibility Property to TrainDirection in order to Show / Hide the right Symbol according to Direction.

I think to implement a converter this way:

class TrainDirectionToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var direction = (TrainDirection)value;
        switch (direction)
        {
            case TrainDirection.Unknown:
                return Application.Current.FindResource("TrainDirectionUnknown");

            case TrainDirection.None:
                return Application.Current.FindResource("TrainDirectionNone");

            case TrainDirection.Left:
                return Application.Current.FindResource("TrainDirectionLeft");

            case TrainDirection.Right:
                return Application.Current.FindResource("TrainDirectionRight");

            default: throw new ArgumentException($"Unsupported TranDirection value: {direction}");
        }
    }

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

There is also a ResourceDictionary where custom styles are defined for the App.

How can I implement this binding ?

Some explanations would be very helpful cause I'm just beginning in c# and WPF programming

like image 715
Amibluesky Avatar asked Jul 19 '18 22:07

Amibluesky


1 Answers

No need for a converter, you can just use data triggers i.e. something like this:

    <Path x:Name="TrainToRight" Data="M80,160L220,160 270,190 220,220 80,220"  Stretch="Fill"  StrokeThickness="2" Opacity="0.9">
        <Path.Style>
            <Style TargetType="{x:Type Path}">
                <Setter Property="Visibility" Value="Hidden" /> <!-- default value -->
                <Style.Triggers>
                    <DataTrigger Binding="{Binding TrainDirectionProperty}" Value="Unknown">
                        <Setter Property="Visibility" Value="Visible" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Path.Style>
    </Path>

This has to be done once for each path.

Alternatively, you can also just declare a single path and use the DataTriggers to set the "Data" property to the path data you're after:

<Path Stretch="Fill" StrokeThickness="2" Stroke="Black" Opacity="0.9">
    <Path.Style>
        <Style TargetType="{x:Type Path}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding TrainDirectionProperty}" Value="Left">
                    <Setter Property="Data" Value="M130,160L260,160 260,220 130,220 80,190z" />
                </DataTrigger>
                <DataTrigger Binding="{Binding TrainDirectionProperty}" Value="Right">
                    <Setter Property="Data" Value="M80,160L220,160 270,190 220,220 80,220" />
                </DataTrigger>
                <!-- etc -->
            </Style.Triggers>
        </Style>
    </Path.Style>
</Path>

Yet another possibility is to create a common style that all your paths use. In this case you'll need some other way to differentiate them, and one way to do that is by storing their value in the "Tag" property, which you can use for arbitrary custom data:

<Path Tag="{x:Static vm:TrainDirection.Right}" Data="M80,160L220,160 270,190 220,220 80,220" Style="{StaticResource TrainDirectionStyle}" />
<Path Tag="{x:Static vm:TrainDirection.Left}" Data="M130,160L260,160 260,220 130,220 80,190z" Style="{StaticResource TrainDirectionStyle}" />
... etc...

Then you create a style which compares the bound value to the value in the Tag and sets the visibility accordingly:

<Style x:Key="TrainDirectionStyle" TargetType="{x:Type Path}">
    <Setter Property="Stretch" Value="Fill" />
    <Setter Property="StrokeThickness" Value="2" />
    <Setter Property="Stroke" Value="Black" />
    <Setter Property="Opacity" Value="0.9" />
    <Setter Property="Visibility" Value="Hidden" />
    <Style.Triggers>
        <DataTrigger Value="True">
            <DataTrigger.Binding>
                <MultiBinding Converter="{StaticResource EqualityConverter}">
                    <Binding Path="TrainDirectionProperty" />
                    <Binding RelativeSource="{RelativeSource Self}" Path="Tag" />
                </MultiBinding>
            </DataTrigger.Binding>
            <Setter Property="Visibility" Value="Visible" />
        </DataTrigger>
    </Style.Triggers>
</Style>

This is one of the rare cases where a converter is warranted, but it's a generic equality comparator that can be used in other parts of your code as opposed to specifically supporting this one case only:

public class EqualityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return object.Equals(values[0], values[1]);
    }

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

My preferred option would probably be #2, because it minimizes the number of bindings and GUI objects while still being relatively easy to read and understand.

like image 129
Mark Feldman Avatar answered Sep 20 '22 11:09

Mark Feldman