これを行うための本当にエレガントな方法は見つかりませんでしたが、Mindscapeの開発者と話をしたところ、MindscapePropertyGridで機能する大雑把で機能的な方法があります。
まず、フラグ列挙型エディター自体のテンプレートを作成します。これは、WPFプロパティグリッドライブラリのEnumValuesConverterを使用して入力された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>
次に、フラグがオンかオフかに応じてチェックボックスをオンとして表示する必要があります。これには2つのことが必要です。1つは、手元のフラグとコンテキスト値の両方を考慮できるIMultiValueConverterであり、もう1つは、個々のチェックボックスがコンテキスト値を読み取る方法です。(コンテキスト値とは、実際のプロパティ値を意味します。たとえば、コンテキスト値は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();
}
}
コンテキスト値を伝播するために、ショートカットを使用してタグを使用します。より意味のある名前で添付プロパティを作成することをお勧めします。
これで、コントロールは設定されているフラグのチェックを表示しますが、チェックボックスをオンまたはオフにしたときに値はまだ更新されません。残念ながら、これを機能させるために私が見つけた唯一の方法は、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に伝播し、そこからプロパティの値に伝播するためです。
イベントハンドラーはほとんど明白ですが、1つのしわがあります。
<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に変更できますが、折りたたまれた表示を処理するために追加の作業を行う必要があります。これについては詳しく説明していません。