web-dev-qa-db-ja.com

WPFデータバインディング:XAMLを使用して列挙型をコンボボックスにデータバインドする方法

私はクラスを持っています:

public class AccountDetail
{
    public DetailScope Scope
    {
        get { return scope; }
        set { scope = value; }
    }

    public string Value
    {
        get { return this.value; }
        set { this.value = value; }
    }

    private DetailScope scope;
    private string value;

    public AccountDetail(DetailScope scope, string value)
    {
        this.scope = scope;
        this.value = value;
    }
}

そして列挙型:

public enum DetailScope
{
    Private, 
    Business, 
    OtherDetail
}

最後に、.xamlファイルがあります。

<Window x:Class="Gui.Wpf.Window1"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="Test" 
    SizeToContent="WidthAndHeight">

    <Grid>
        <ComboBox 
            Name="ScopeComboBox" 
            Width="120" 
            Height="23" 
            Margin="12" />
    </Grid>
</Window>

私は2つのことをしたいと思います:

  1. DetailsScope列挙値をコンボボックス値にデータバインドしたいと思います。最後の列挙値はOther detailではなくOtherDetailになるため、列挙値を直接バインドしたくありません(スペース文字と小文字「d」が追加されています)。
  2. コンボボックスで選択した値をAccountDetailオブジェクトのインスタンスで指定した値にデータバインドしたいと思います。

手伝ってくれませんか?ありがとう。

更新:この投稿を見つけました http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx 。同様のものが必要です。

24
Boris

これを行う非常に簡単な方法は、ObjectDataProviderを使用することです。

<ObjectDataProvider MethodName="GetValues"
                    ObjectType="{x:Type sys:Enum}"
                    x:Key="DetailScopeDataProvider">
    <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="local:DetailScope" />
    </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

ObjectDataProviderをComboBoxのItemsSourceとして使用し、SelectedItemをScopeプロパティにバインドして、各ComboBoxItemの表示にコンバーターを適用します。

<ComboBox Name="ScopeComboBox"
          ItemsSource="{Binding Source={StaticResource DetailScopeDataProvider}}"
          SelectedItem="{Binding Scope}"
          Width="120"
          Height="23"
          Margin="12">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource CamelCaseConverter}}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

そして、コンバーターでは、 this 質問にあるCamelCase文字列スプリッターにRegexを使用できます。私は最も高度なバージョンを使用しましたが、おそらくよりシンプルなバージョンを使用できます。 OtherDetail + regex =その他の詳細。戻り値を低くして、最初の文字である大文字の文字列を返すと、期待した結果が得られるはずです。

public class CamelCaseConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string enumString = value.ToString();
        string camelCaseString = Regex.Replace(enumString, "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ").ToLower();
        return char.ToUpper(camelCaseString[0]) + camelCaseString.Substring(1);
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}
41
Fredrik Hedblad

私がいつもやっているやり方は以下の通りです。このソリューションの優れている点は、完全に汎用的であり、あらゆる列挙型に再利用できることです。

1)列挙を定義するときは、いくつかのカスタム属性を使用して少し情報を提供します。この例では、Browsable(false)を使用して、この列挙子が内部にあることを示しています。このオプションをコンボボックスに表示したくありません。 Description( "")を使用すると、列挙の表示名を指定できます。

public enum MyEnumerationTypeEnum
  {
    [Browsable(false)]
    Undefined,
    [Description("Item 1")]
    Item1,
    [Description("Item 2")]
    Item2,
    Item3
  }

2)EnumerationManagerを呼び出したクラスを定義します。これは、列挙型を分析して値のリストを生成する汎用クラスです。列挙子のBrowsableがfalseに設定されている場合、スキップされます。 Description属性がある場合は、説明文字列を表示名として使用します。説明が見つからない場合は、列挙子のデフォルトの文字列が表示されます。

public class EnumerationManager
  {
    public static Array GetValues(Type enumeration)
    {
      Array wArray = Enum.GetValues(enumeration);
      ArrayList wFinalArray = new ArrayList();
      foreach(Enum wValue in wArray)
      {
        FieldInfo fi = enumeration.GetField(wValue.ToString());
        if(null != fi)
        {
          BrowsableAttribute[] wBrowsableAttributes = fi.GetCustomAttributes(typeof(BrowsableAttribute),true) as BrowsableAttribute[];
          if(wBrowsableAttributes.Length > 0)
          {
            //  If the Browsable attribute is false
            if(wBrowsableAttributes[0].Browsable == false)
            {
              // Do not add the enumeration to the list.
              continue;
            }        
          }

          DescriptionAttribute[] wDescriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute),true) as DescriptionAttribute[];
      if(wDescriptions.Length > 0)
      {
        wFinalArray.Add(wDescriptions[0].Description);
      }
      else 
        wFinalArray.Add(wValue);
        }
      }

      return wFinalArray.ToArray();
    }
  }

3)XAMLでResourceDictionaryにセクションを追加します

  <ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type l:EnumerationManager}" x:Key="OutputListForMyComboBox">
      <ObjectDataProvider.MethodParameters>
           <x:Type TypeName="l:MyEnumerationTypeEnum" />
      </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>

4)次に、コンボボックスのItemsSourceを、リソースディクショナリで定義したこのキーにバインドします。

<ComboBox Name="comboBox2" 
          ItemsSource="{Binding Source={StaticResource OutputListForMyComboBox}}" />

上記の列挙を使用してこのコードを試すと、コンボボックスに3つの項目が表示されます。

Item 1
Item 2
Item3

お役に立てれば。

編集:LocalizableDescriptionAttributeの実装を追加し、代わりにそれを使用する場合、私が使用していたDescription属性は完璧です。

11
Liz

これが解決策です。すべての可能性を含むプロパティ(リスト)を作成し、そのプロパティにComboBoxをバインドします。

XAMLの場合:

<ComboBox
    Name="ScopeComboBox" 
    Width="120" 
    Height="23" 
    Margin="12" 
    ItemsSource="{Binding Path=AccountDetailsProperty}"
    DisplayMemberPath="Value"/>

そして後ろのコードで:

public partial class Window1 : Window
{
    public Window1() 
    {
        AccountDetailsProperty = new List<AccountDetail>()
        {
            new AccountDetail(DetailScope.Business, "Business"),
            new AccountDetail(DetailScope.OtherDetail, "Other details"),
            new AccountDetail(DetailScope.Private, "Private"),
        };

        InitializeComponent();
        this.DataContext = this;
    }

    public List<AccountDetail> AccountDetailsProperty { get; set; }
}
2
Nicolas

これには値コンバーターを使用します。これにより、コンバーターを使用して直接バインドできます。Convertの実装を変更して、列挙型の「より優れた」人間が読める形式にする、つまり大文字で分割することができます。

このアプローチについての完全な記事 here があります。

  public class MyEnumToStringConverter : IValueConverter
  {
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
         return value.ToString();
     }

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
     {
         return (MyEnum) Enum.Parse( typeof ( MyEnum ), value.ToString(), true );
     }
  }
1
BrokenGlass