我还没有找到一种真正优雅的方法来实现这一点,但是通过与Mindscape的开发人员交谈,这里有一些粗糙但功能性很强的东西可以与Mindscape PropertyGrid一起使用。
首先,我们为标志枚举编辑器本身创建一个模板。这是使用WPF属性网格库中的EnumValueConverter填充的项控件:
<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,这样它就可以同时考虑手头的标志和上下文值;第二,一种单独的复选框读取上下文值的方法。(根据上下文值,我指的是实际属性值。例如,上下文值可能是flag1 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=“flageditoremplate”>
<itemscontrol name=“ic”itemssource=“绑定值,converter=staticresource evc”tag=“绑定值,模式=twoway”>
<itemsControl.itemTemplate>
<数据模板>
<checkbox content=“绑定”tag=“绑定标记,elementname=ic,mode=twoway”checked=“checkbox_checked”unchecked=“checkbox_unchecked”>
<checkbox.ischecked>
<multibinding converter=“staticresource fc”mode=“单向”>
<绑定/>
<binding path=“tag”elementname=“ic”/>
</multibinding>
</checkbox.ischecked>
</checkbox>
</datatemplate>
</itemscontrol.itemstemplate>
</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在内部无法将该值转换为枚举类型。这里curses是我的枚举类型。如果您想要一个完全灵活的、类型不可知的编辑器,您需要在外部提供它,例如作为复选框上的附加属性。您可以使用转换器来推断这一点,也可以从编辑器EditContext传播它。
最后,我们需要把这个连接到网格上。您可以在逐个属性的基础上执行此操作:
<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更改为组合框,尽管您需要做一些额外的工作来处理折叠的显示;我没有详细探讨过这个问题。