绑定标志枚举到包含复选框的列表框

问题描述 投票:2回答:2

我有这个枚举枚举-

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

我想在视图上显示此枚举的值。我想创建一个列表框并将其源绑定到该枚举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应保持未选中状态。

c# wpf listbox wpf-controls itemscontrol
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而不是DepartmentsPanel的DataContext设置为DataObject已创建。必须执行此步骤才能显示“原始值”。


0
投票

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

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

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

转换器实例应获取枚举的类型以及是否为Flags枚举。

 <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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.