WPF数据绑定到标志枚举(在PropertyGrid中)
-
16-09-2019 - |
题
我需要有选择多个值的能力如从一个WPF视图一个标志枚举的性质(全部是它在一个PropertyGrid中)。
所讨论的性质的动态强>可以被用作在类型的特性,并且没有预定义的DataTemplates将在运行时发现的即可。 (一个DataTemplate它可以检测是否枚举的标志可以证明是有益的,但是从我的理解,我需要提前了解的标志枚举类型来实现这一点,不会是这种情况)。
我已经尝试了一些专有的和开源属性网格WPF和无的似乎支持“标志”归因枚举类型现成的。
一个解决这个问题是什么,让我来给数据绑定+选择多个值表示标志枚举用于任何商业或开源的WPF的PropertyGrid。
<强>代码:强>
实施例属性类型:
public class PropertyTypeOne
{
public PropertyTypeOne()
{
IntProp = 1;
InProp2 = 2;
BoolProp = true;
Boolprop2 = false;
StringProp = "string1";
DoubleProp = 2.3;
EnumProp = FlagEnumDataTYpe.MarketDepth;
}
public int IntProp { get; set; }
public int InProp2 { get; set; }
public bool BoolProp { get; set; }
public bool BoolProp2 { get; set; }
public string StringProp { get; set; }
public double DoubleProp { get; set; }
//This is the property in question
public FlagEnumDataType EnumProp { get; set; }
}
实施例标志枚举类型:
[Flags]
public enum FlagEnumDataType : byte
{
None = 0,
Trade = 1,
Quote = 2,
MarketDepth = 4,
All = 255
}
注意:强>
如果该解决方案使得使用开源WPF的PropertyGrid(的 http://www.codeplex.com/wpg )我将实施改变/添加回控制。
<强>感谢。强>
解决方案
我还没有发现这样做的真正优雅的方式,但是从与开发商的思维空间说话,这里的东西原油,但多数民众赞成功能的工作原理与思维空间的PropertyGrid。
首先,我们创建了旗枚举编辑器本身的模板。这是使用EnumValuesConverter从WPF属性网格库填充一个ItemsControl:
<ms:EnumValuesConverter x:Key="evc" />
<local:FlaggyConverter x:Key="fc" />
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}">
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
现在我们需要作为根据标记是否打开或关闭检查显示的复选框。这需要两件事情:第一,一个IMultiValueConverter因此它可以同时考虑手头的标志和背景值,二是个人的复选框的方式来阅读上下文值。 (通过上下文值我指的是实际的属性值如背景值可能是标志1 | Flag4的| Flag32。)这里的转换器:
public class FlaggyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int flagValue = (int)values[0];
int propertyValue = (int)values[1];
return (flagValue & propertyValue) == flagValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
有关传播的上下文值,我要去走捷径和使用标记。你可能更喜欢用一个更有意义的名称创建一个附加属性。
现在控制系统将显示为所设置的标志检查,但是当你点击打开或关闭复选框将没有更新的价值。不幸的是,我发现,使这项工作的唯一途径是通过处理经过和未选中的事件和手工设置上下文值。为了做到这一点,我们需要把上下文值在一个地方,它可以从复选框事件处理程序进行更新。这意味着双向的复选框的属性绑定到上下文值。再次,我将使用标签虽然你可能想要的东西有点清洁;同时,我将使用直接事件处理,尽管这取决于你的设计,你可能想这个包裹成一个附加的行为(这会工作特别好,如果你正在创建附加属性携带上下文值)。
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}" Tag="{Binding Value, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Tag="{Binding Tag, ElementName=ic, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource fc}" Mode="OneWay">
<Binding />
<Binding Path="Tag" ElementName="ic" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
请注意双向标签的结合:是这样的话,当我们从事件处理代码设置标签,它传播回ic.Tag,并从那里到属性的值
在事件处理程序大多是明显的,但与一种起皱:
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}" Tag="{Binding Value, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Tag="{Binding Tag, ElementName=ic, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource fc}" Mode="OneWay">
<Binding />
<Binding Path="Tag" ElementName="ic" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
事件处理程序:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox cb = (CheckBox)sender;
int val = (int)(cb.Tag);
int flag = (int)(cb.Content);
val = val | flag;
cb.Tag = (Curses)val;
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
CheckBox cb = (CheckBox)sender;
int val = (int)(cb.Tag);
int flag = (int)(cb.Content);
val = val & ~flag;
cb.Tag = (Curses)val;
}
请注意铸造设置cb.Tag时。没有这一点,WPF内部未能试图传播回源时的值转换为枚举类型。这里诅咒是我的枚举类型。如果你想要一个完全灵活的,类型无关的编辑器,你要提供外部,例如在复选框中有一个附加属性。你可以或者推断此使用的转换器或从编辑器编辑上下文传播它。
最后,我们需要这个挂钩到电网。你可以做到这一点无论对财产的属性基础:
<ms:PropertyGrid>
<ms:PropertyGrid.Editors>
<ms:PropertyEditor PropertyName="Curses" EditorTemplate="{StaticResource FlagEditorTemplate}" />
</ms:PropertyGrid.Editors>
</ms:PropertyGrid>
,或通过使用一个聪明的编辑声明挂钩,其类型有一个FlagsAttribute所有属性。有关创建和使用智能编辑信息,看到的 http://www.mindscape.co.nz/blog/index.php/2008/04/30/smart-editor-declarations-in-the-wpf-property -grid / 。
如果你想保存你可以改变的ItemsControl到一个ComboBox空间虽然你需要做一些额外的工作来处理倒塌的显示;我还没有详细探讨这一点。
其他提示
在互联网上找到,稍有改善,但没有时间来测试它。
/// <summary>
/// Two-way conversion from flags to bool and back using parameter as mask
/// Warning: The trick is in storing value locally between calls to Convert and ConvertBack
/// You must have a single instance of this converter per flags property per object
/// Do not share this converter between different objects or properties
/// Typical usage:
/// [Flags] enum FlagType { None = 0, Trade = 1, Quote = 2, Report = 4, All = 255 }
/// <local:EditableFlagsToBooleanConverter x:Key="FlagsToBooleanConverter" />
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Trade}}" >Trade</CheckBox>
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Quote}}" >Quote</CheckBox>
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Report}}" >Report</CheckBox>
/// </summary>
public class EditableFlagsToBooleanConverter : IValueConverter
{
private ulong _target;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is Enum && value is Enum)
{
var mask = (ulong) parameter;
_target = (ulong) value;
return ((mask & _target) != 0);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool && parameter is Enum)
{
var mask = (ulong)parameter;
if ((bool)value)
{
_target |= mask;
}
else
{
_target &= ~mask;
}
return _target;
}
return Binding.DoNothing;
}
}