我正在创建WPF实用程序库,但我仍然坚持如何创建一个易于使用的枚举转换器,这将有助于将任何枚举绑定到ComboBox控件以及使用[Flags]声明枚举时属性,ComboBox将包含绑定到它的复选框数据。解决方案需要是通用的,以便它可以很容易地用于任何枚举,我想它需要经验丰富的WPF开发人员的新眼睛。
到目前为止,我已经提出了ObjectDataProvider作为枚举项和2个转换器的组合,一个用于ItemsSource,另一个用于SelectedValue。 我使用CollectionView作为ItemsSource的基类来使用CurrentChanged,但它不起作用,因为我试图在收集任何CheckBox被检查后没有转换器的ConvertBack被执行时尝试引发OnCurrentChanged。
问题是,除非我更改所选项目(不勾选复选框),否则不会更新ComboBox SelectedValue属性绑定到的数据绑定属性。 我设置了IsSynchronizedWithCurrentItem但它没有用。
如何从数据绑定对象强制更新SelectedValue?我有选择的自由,因此解决方案可能涉及自定义附加属性,继承自ComboBox等。作为WPF的新手,我认为虽然解决方案几乎正确,因为它几乎可以工作,但可以有一种更简单的方法来实现它。 / p>
另一个问题是如何自定义ComboBox上显示的文本,以便它包含勾选复选框的聚合选择。
我上传了VS2012项目here。项目的结构类似于实际应用程序,资源是单独保存的(为简单起见,在App.xaml中)。
抱歉,我没有包含任何代码,但涉及的代码很多。
下面是绑定到标准枚举的CheckBox的XAML声明(尽管使用的枚举是[Flags]),它工作正常,并且CheckBox绑定到[Flags]枚举。
<ComboBox ItemsSource="{Binding Source={StaticResource someItemsSource}}"
SelectedValue="{Binding BindToSomeItems}"
Style="{StaticResource enumComboBox}"/>
<ComboBox ItemsSource="{Binding BindToSomeItems, Converter={local:LocalizedFlagsEnumConverter}}"
IsSynchronizedWithCurrentItem="True"
SelectedValue="{Binding BindToSomeItems, Mode=TwoWay, Converter={local:SelectedFlagsEnumConverter}}"
Style="{StaticResource flagsComboBox}"
/>
资源(解释价值转换器的使用):
<Application.Resources>
<Style x:Key="enumComboBox" TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Path=.,Mode=OneWay, Converter={local:LocalizedEnumConverter}}" Style="{x:Null}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="flagsComboBox" TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=Checked,Mode=TwoWay}"/>
<TextBlock Text="{Binding Path=Name,Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
答案 0 :(得分:3)
我使用自定义复选框来执行此操作。它完美而简单。请参阅课程顶部的评论以了解如何使用它。
希望它有所帮助...
using System;
using System.Windows;
using System.Windows.Controls;
namespace HQ.Wpf.Util.MyControl
{
/// <summary>
/// Usage: Bind EnumFlag and Two way binding on EnumValue instead of IsChecked
/// Example: <myControl:CheckBoxForEnumWithFlagAttribute
/// EnumValue="{Binding SimulationNatureTypeToCreateStatsCacheAtEndOfSimulation, Mode=TwoWay}"
/// EnumFlag="{x:Static Core:SimulationNatureType.LoadFlow }">Load Flow results</myControl:CheckBoxForEnumWithFlagAttribute>
/// </summary>
public class CheckBoxForEnumWithFlagAttribute : CheckBox
{
// ************************************************************************
public static DependencyProperty EnumValueProperty =
DependencyProperty.Register("EnumValue", typeof(object), typeof(CheckBoxForEnumWithFlagAttribute), new PropertyMetadata(EnumValueChangedCallback));
public static DependencyProperty EnumFlagProperty =
DependencyProperty.Register("EnumFlag", typeof(object), typeof(CheckBoxForEnumWithFlagAttribute), new PropertyMetadata(EnumFlagChangedCallback));
// ************************************************************************
public CheckBoxForEnumWithFlagAttribute()
{
base.Checked += CheckBoxForEnumWithFlag_Checked;
base.Unchecked += CheckBoxForEnumWithFlag_Unchecked;
}
// ************************************************************************
private static void EnumValueChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var checkBoxForEnumWithFlag = dependencyObject as CheckBoxForEnumWithFlagAttribute;
if (checkBoxForEnumWithFlag != null)
{
checkBoxForEnumWithFlag.RefreshCheckBoxState();
}
}
// ************************************************************************
private static void EnumFlagChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var checkBoxForEnumWithFlag = dependencyObject as CheckBoxForEnumWithFlagAttribute;
if (checkBoxForEnumWithFlag != null)
{
checkBoxForEnumWithFlag.RefreshCheckBoxState();
}
}
// ************************************************************************
public object EnumValue
{
get { return GetValue(EnumValueProperty); }
set { SetValue(EnumValueProperty, value); }
}
// ************************************************************************
public object EnumFlag
{
get { return GetValue(EnumFlagProperty); }
set { SetValue(EnumFlagProperty, value); }
}
// ************************************************************************
private void RefreshCheckBoxState()
{
if (EnumValue != null)
{
if (EnumValue is Enum)
{
Type underlyingType = Enum.GetUnderlyingType(EnumValue.GetType());
dynamic valueAsInt = Convert.ChangeType(EnumValue, underlyingType);
dynamic flagAsInt = Convert.ChangeType(EnumFlag, underlyingType);
base.IsChecked = ((valueAsInt & flagAsInt) > 0);
}
}
}
// ************************************************************************
private void CheckBoxForEnumWithFlag_Checked(object sender, RoutedEventArgs e)
{
RefreshEnumValue();
}
// ************************************************************************
void CheckBoxForEnumWithFlag_Unchecked(object sender, RoutedEventArgs e)
{
RefreshEnumValue();
}
// ************************************************************************
private void RefreshEnumValue()
{
if (EnumValue != null)
{
if (EnumValue is Enum)
{
Type underlyingType = Enum.GetUnderlyingType(EnumValue.GetType());
dynamic valueAsInt = Convert.ChangeType(EnumValue, underlyingType);
dynamic flagAsInt = Convert.ChangeType(EnumFlag, underlyingType);
dynamic newValueAsInt = valueAsInt;
if (base.IsChecked == true)
{
newValueAsInt = valueAsInt | flagAsInt;
}
else
{
newValueAsInt = valueAsInt & ~flagAsInt;
}
if (newValueAsInt != valueAsInt)
{
object o = Enum.ToObject(EnumValue.GetType(), newValueAsInt);
EnumValue = o;
}
}
}
}
// ************************************************************************
}
}