【问题标题】:WPF Binding enum to VisibilityWPF 将枚举绑定到可见性
【发布时间】:2018-12-28 04:15:40
【问题描述】:

我正在为 WPF 应用程序使用 MVVM 模式(入门)

我在模型的类Train.cs 中定义了一个名为TrainDirection 的属性:

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

在视图中,我想根据枚举的值显示/隐藏代表trainObjectsymbol

我创建了一个“用户控件”:

<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>

我想我需要 trainDirectionToVisibilityConverter 之类的东西来将可见性属性绑定到 TrainDirection 以便根据方向显示/隐藏正确的符号。

我想以这种方式实现转换器:

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();
    }
}

还有一个 ResourceDictionary,其中为 App 定义了自定义样式。

我怎样才能实现这个绑定?

一些解释会很有帮助,因为我刚刚开始使用 c# 和 WPF 编程

【问题讨论】:

    标签: c# wpf xaml binding


    【解决方案1】:

    不需要转换器,您可以使用数据触发器,例如:

        <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>
    

    每条路径必须执行一次。

    或者,您也可以只声明一个路径并使用 DataTriggers 将“Data”属性设置为您所追求的路径数据:

    <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>
    

    另一种可能性是创建一个所有路径都使用的通用样式。在这种情况下,您需要一些其他方法来区分它们,其中一种方法是将它们的值存储在“Tag”属性中,您可以将其用于任意自定义数据:

    <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...
    

    然后您创建一个样式,将绑定值与标签中的值进行比较,并相应地设置可见性:

    <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>
    

    这是需要转换器的少数情况之一,但它是一个通用的相等比较器,可用于代码的其他部分,而不是专门支持这种情况:

    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();
        }
    }
    

    我的首选选项可能是 #2,因为它最大限度地减少了绑定和 GUI 对象的数量,同时仍然相对容易阅读和理解。

    【讨论】:

    • 优秀的解决方案。
    • 如果您想根据枚举值在几个面板之间切换,第一个解决方案效果很好。只需要使用Collapsed 而不是Hidden。谢谢!
    • @AlexisLeclerc true,尽管在这种情况下我可能会选择使用 ContentControl 并使用 DataTriggers 根据枚举值设置内容。可见性的问题是所有的 GUI 元素及其绑定等仍然在可视化树中......对于任何有很多子控件的东西。
    猜你喜欢
    • 1970-01-01
    • 2011-04-12
    • 2011-07-03
    • 1970-01-01
    • 2012-05-23
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    相关资源
    最近更新 更多