Подтвердить что ты не робот

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>

Я хотел бы сделать две вещи:

  • Я хочу привязать данные DetailsScope значения enum к значениям поля со списком. Я не хочу привязать значения перечисления непосредственно, потому что последнее значение перечисления будет OtherDetail вместо Other detail (добавлен пробельный символ и маленькая буква 'd').
  • Я хочу, чтобы данные привязывали выбранное значение в поле со списком к указанному в экземпляр объекта AccountDetail.

Не могли бы вы мне помочь? Благодарю.

Обновление: я нашел этот пост http://blogs.msdn.com/b/wpfsdk/archive/2007/02/22/displaying-enum-values-using-data-binding.aspx. Мне нужно что-то подобное.

4b9b3361

Ответ 1

Довольно простой способ сделать это - использовать ObjectDataProvider

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

Используйте ObjectDataProvider как ItemsSource для ComboBox, привяжите 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>

И в конвертере вы можете использовать Regex для разделителя строк CamelCase, найденного в этом вопросе. Я использовал самую передовую версию, но вы, вероятно, можете использовать более простой вариант. OtherDetail + regex = Другие детали. Возвращение значения ниже, а затем возврат строки с первым символом UpperCase должен дать ожидаемый результат

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;
    }
}

Ответ 2

То, как я всегда это делал, выглядит следующим образом. Красота этого решения заключается в том, что он является полностью общим и может быть повторно использован для любых типов перечисления.

1) Когда вы определяете перечисление, используйте некоторые пользовательские атрибуты, чтобы дать небольшую информацию. В этом примере я использовал Browsable (false), чтобы указать, что этот перечислитель является внутренним, и я не хочу видеть этот параметр в поле со списком. Описание ("") позволяет мне указать отображаемое имя для перечисления.

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

2) Определите класс, который я назвал EnumerationManager. Это общий класс, который анализирует тип Enumeration и генерирует список значений. Если перечислитель имеет параметр "Доступно для просмотра", то он будет пропущен. Если у него есть атрибут 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) Теперь просто привяжите ItemSource вашего combobox к этому ключу, который мы только что определили в словаре ресурсов

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

Если вы попробуете этот код, используя мое перечисление выше, вы должны увидеть 3 элемента в своем поле со списком:

Item 1
Item 2
Item3

Надеюсь, что это поможет.

редактирует: Если вы добавите реализацию LocalizableDescriptionAttribute и используете это, то атрибут Description, который я использовал, был бы идеальным.

Ответ 3

Вот решение: вы создаете свойство (список), которое содержит все возможности, и вы связываете свой 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; }
}

Ответ 4

Я бы использовал конвертер значений для этого, это позволит вам напрямую связываться с помощью конвертера, вы можете изменить реализацию Convert, чтобы сделать "более приятную" читаемую пользователем презентацию перечислений, т.е. разделить на заглавные символы.

Здесь есть полная статья об этом подходе здесь.

  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 );
     }
  }