我還沒有找到一個真正優雅的方式來做到這一點,但從與Mindscape的開發人員交談,這裏有一些原始的,但功能多數民衆贊成在Mindscape PropertyGrid的作品。
首先,我們爲flag-enum編輯器本身創建一個模板。這是使用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();
}
}
對於傳播的上下文值,我要走捷徑和使用標籤。您可能更願意使用更有意義的名稱創建附加屬性。
現在控制系統將顯示爲所設置的標誌檢查,但尚未更新的價值,當你點擊或關閉複選框。不幸的是,我發現完成這項工作的唯一方法是處理Checked和Unchecked事件並手動設置上下文值。爲了做到這一點,我們需要將上下文值放置在可以從複選框事件處理程序更新的地方。這意味着將複選框的一個屬性雙向綁定到上下文值。再次,我會使用標籤,雖然你可能想要一些更清潔的東西;另外,我將使用直接事件處理,儘管取決於您的設計,您可能希望將其包含到附加行爲中(如果您創建附加屬性以傳遞上下文值,這將會特別有效)。
<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嘗試將其傳播回源時,內部無法將該值轉換爲枚舉類型。這裏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更改爲ComboBox,但您需要做一些額外的工作來處理摺疊的顯示;我沒有詳細探討這一點。