1

WPFビュー(PropertyGrid内のすべて)からのフラグ列挙の性質と同様に、複数の値を選択する機能が必要です。

問題のプロパティは動的であり、プロパティのタイプは実行時に検出されるため、事前定義されたDataTemplatesを使用することはできません。(列挙型がフラグであるかどうかを検出できるDataTemplateは役立つ場合がありますが、私の理解から、これを実現するには事前にFlag Enumタイプを知る必要がありますが、そうではありません)。

私はWPF用の独自のオープンソースプロパティグリッドをいくつか試しましたが、すぐに使用できる「フラグ」属性の列挙型をサポートしているものはないようです。

この問題の解決策は、データバインドして、商用またはオープンソースのWPFPropertyGridのFlagsEnumに複数の値を選択できるようにすることです。

コード:

PropertyTypeの例:

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
}

ノート:

ソリューションでオープンソースのWPFPropertyGrid(http://www.codeplex.com/wpg)を使用する場合は、変更/追加をコントロールに実装します。

ありがとう。

4

2 に答える 2

3

これを行うための本当にエレガントな方法は見つかりませんでしたが、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に変更できますが、折りたたまれた表示を処理するために追加の作業を行う必要があります。これについては詳しく説明していません。

于 2009-10-19T12:49:05.850 に答える
0

インターネットで見つかり、わずかに改善されましたが、まだテストする時間がありませんでした。

/// <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;
    }
} 
于 2010-12-01T18:06:20.777 に答える