2

我有标志枚举这样说 -

[Flags]
public enum Department
{
    None = 0,
    A = 1,
    B = 2,
    C = 4,
    D = 8
}

我想在视图中显示此枚举的值。我想创建一个列表框并将其源绑定到这个 enum 的集合List<Department> Departments。一切都很好,直到我想到有一个绑定到我的 Viewmodel 上的属性的复选框 -

public Department SelectedDepartments { get; set; }

此处的解决方案http://compilewith.net/2008/12/wpf-flagsenumvalueconverter.html为将枚举值绑定到复选框提供了优雅的解决方案,但它有一个限制,即创建的复选框等于列表中枚举值的数量。但是,在我的情况下,我不能在我的 UI 上放置这么多复选框,因为我的 Enum 包含 20 个值(这意味着 UI 上有 20 个复选框)。

我尝试使用但在方法MultiBindingConverter中失败了。ConvertBack我想将复选框的状态与属性 SelectedDepartments 绑定。假设属性值为“A | B”,则应选中 A 和 B 复选框,而 C 和 D 应保持未选中状态。

4

2 回答 2

1

我认为没有使用一些代码隐藏的方法可以做到这一点。

我采用了您在上面链接的示例解决方案,从 MainWindow.xaml 中删除了所有 CheckBox,将以下方法添加到 MainWindow.xaml.cs 并从MainWindow构造函数中调用它:

    private void AddCheckBoxes()
    {
        var converter = new FlagsEnumValueConverter();
        foreach (Department dept in Enum.GetValues(typeof(Department)))
        {
            if (dept != Department.None)
            {
                var binding = new Binding()
                {
                    Path = new PropertyPath("Department"),
                    Converter = converter,
                    ConverterParameter = dept
                };

                var checkBox = new CheckBox() { Content = dept.ToString() };
                checkBox.SetBinding(CheckBox.IsCheckedProperty, binding);
                DepartmentsPanel.Children.Add(checkBox);
            }
        }
    }

此方法完成创建所有复选框的工作,除了None. 然后我可以将更多部门添加到Department枚举中,重新运行解决方案并查看新添加部门的其他复选框。

我必须对此解决方案进行一些进一步的小改动才能使其完全正常工作。您可能需要也可能不需要对代码进行这些更改。首先,我让DataObject类实现INotifyPropertyChanged。其次,我在 MainWindow.xaml 中重写了 XAML,如下所示:

<StackPanel>
    <StackPanel x:Name="DepartmentsPanel" />
    <TextBlock Margin="5,20,0,0">
        <TextBlock Text="Raw Value:" FontWeight="Bold" />
        <TextBlock Text="{Binding Department}" />
    </TextBlock>
</StackPanel>

(基本上,我将现有的 DepartmentsPanel 包装在另一个 StackPanel 中,并将“原始值”显示移动到这个外部 StackPanel 中。)最后,我将整个 MainWindow 的 DataContext 而不是 的 DataContext 设置DepartmentsPanelDataObject创建的。这一步是使“原始值”显示工作所必需的。

于 2012-04-08T13:56:02.860 回答
0

我创建了一个 IValueConverter,它支持直接绑定到枚举,无需代码隐藏或辅助类。它只有两个限制:

  • 每个源属性都必须使用一个转换器实例。如果模型包含更多相同枚举类型的属性,则每个属性都需要使用单独的转换器实例。这可以通过在 XAML 中实例化转换器来完成。
  • 当枚举类型是标志枚举时,它必须是整数。

该解决方案基于经验事实,即在 ConvertBack 之前始终先有一个 Convert。如果 ConvertBack 更改了值,则始终存在 Convert。这仅在模型上正确实施 INotifyPropertyChanged 时才有效。因此,在两次调用之间,最后一个已知值可以存储在转换器中并在 ConvertBack 方法中使用。

转换器实例应该获取枚举的类型以及它是否是标志枚举。

 <EnumToCheckedConverter x:Key="InstanceName" Type="{x:Type MyEnum}" Flags="True" />

然后可以使用此转换器绑定复选框。

 <CheckBox Content="ValueText" IsChecked="{Binding Source, Converter={StaticResource InstanceName}, ConverterParameter=Value}"/>

单选按钮可以使用具有 Flags="False" 的实例通过相同的机制绑定

转换器的源代码

public class EnumToCheckedConverter : IValueConverter
{
    public Type Type { get; set; }
    public int? LastValue { get; private set; }
    public bool Flags { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value.GetType() == Type)
        {
            try
            {
                var parameterValue = Enum.Parse(Type, parameter as string);

                if (Flags == true)
                {
                    var intParameter = (int)parameterValue;
                    var intValue = (int)value;
                    LastValue = intValue;

                    return (intValue & intParameter) == intParameter;
                }
                else
                {
                    return Equals(parameterValue, value);
                }
            }
            catch (ArgumentNullException)
            {
                return false;
            }
            catch (ArgumentException)
            {
                throw new NotSupportedException();
            }
        }
        else if (value == null)
        {
            return false;
        }

        throw new NotSupportedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value is bool check)
        {
            if (check)
            {
                try
                {
                    if (Flags == true && LastValue.HasValue)
                    {
                        var parameterValue = Enum.Parse(Type, parameter as string);
                        var intParameter = (int)parameterValue;

                        return Enum.ToObject(Type, LastValue | intParameter);
                    }
                    else
                    {
                        return Enum.Parse(Type, parameter as string);
                    }
                }
                catch (ArgumentNullException)
                {
                    return Binding.DoNothing;
                }
                catch (ArgumentException)
                {
                    return Binding.DoNothing;
                }
            }
            else
            {
                try
                {
                    if (Flags == true && LastValue.HasValue)
                    {
                        var parameterValue = Enum.Parse(Type, parameter as string);
                        var intParameter = (int)parameterValue;

                        return Enum.ToObject(Type, LastValue ^ intParameter);
                    }
                    else
                    {
                        return Binding.DoNothing;
                    }
                }
                catch (ArgumentNullException)
                {
                    return Binding.DoNothing;
                }
                catch (ArgumentException)
                {
                    return Binding.DoNothing;
                }
            }
        }

        throw new NotSupportedException();
    }
}
于 2020-01-07T23:18:01.377 回答