我有这个枚举枚举-
[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应保持未选中状态。
我认为不使用任何后台代码就没有办法做到这一点。
我从上面链接到示例解决方案,从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
已创建。必须执行此步骤才能显示“原始值”。
我创建了一个IValueConverter,它支持直接绑定到枚举,而无需代码隐藏或帮助程序类。它只有两个限制:
该解决方案基于以下经验事实:在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();
}
}