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

时间:2012-04-08 09:46:37

标签: c# wpf listbox wpf-controls itemscontrol

我有标志enum说这个 -

[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提供了将枚举值绑定到复选框的优雅解决方案,但它有一个限制,即创建复选框等于列表中的枚举值数量。 但是,在我的情况下,由于我的Enum包含20个值(因此在UI上有20个复选框),我无法承受在我的UI上放置如此多的复选框。

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

2 个答案:

答案 0 :(得分: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中,并将'Raw Value'显示移动到这个外层StackPanel中。)最后,我设置了整个MainWindow的DataContext,而不是DepartmentsPanel的DataContext。 ,到创建的DataObject。此步骤对于使“原始值”显示有效是必要的。

答案 1 :(得分:0)

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

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

该解决方案基于以下经验事实:始终在ConvertBack之前始终存在Convert。如果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();
    }
}