Привязать текстовый блок к значению словаря для ключа в XAML? - PullRequest
6 голосов
/ 27 августа 2011

У меня есть модель со свойством enum (в данном случае относится к Правилам экспортного контроля). При отображении значения для пользователя, я хочу показать соответствующую строку. Иногда это происходит в ComboBox (где пользователь может выбрать значение), а иногда - в TextBlock (где оно доступно только для чтения).

Пример: для ExportRegulationType.EAR я хочу отобразить "EAR", а для ExportRegulationType.DoNotExport я хочу отобразить "Do Not Export". Обратите внимание, что у меня нет потребности в локализации языка, но я осознаю проблему ...

В настоящее время в моем ViewModel есть свойство, которое возвращает строку на основе текущего значения перечисления, а также другое свойство, которое возвращает Dictionary<ExportRegulationType, string>. Для ComboBox я могу связать ItemsSource со свойством словаря, а для TextBlocks я могу связать со свойством string. Это работает, но немного неуклюже.

Два вопроса:

1) Мне кажется, что я должен иметь возможность объявить словарь (с ключами и значениями) как статический ресурс в XAML (возможно, в App.xaml) и использовать его для ItemsSource для версии ComboBox. Тем не менее, я не могу понять, как объявить и сослаться на такую ​​вещь. Как я могу это сделать?

2) Предполагая, что все вышеперечисленное выполнено, я думаю, я мог бы также установить привязку к текстовому блоку, поэтому, основываясь на свойстве enum, он будет искать строку в словаре.

Я видел следующие вопросы, относящиеся к статическому или динамическому значению перечисления. Первое неадекватно, а второе не отвечено ...

Они должны быть только для XAML и позволят мне удалить методы из моей ViewModel (имея только одно открытое перечисляемое свойство ExportRegulationType. Возможно ли это?

Редактировать: Дополнительная информация:

В приложении у меня будет много разных наборов видов, моделей и моделей представления. Тем не менее, поскольку правила экспортного контроля являются распространенным и последовательным требованием, я использую композицию, чтобы сохранить ее СУХОЙ. т.е. модели A и B обе имеют модель ExportControl. Модели ViewModel A1, A2, B1 и B2 будут иметь ExportControlViewModel. Представления будут иметь элементы управления, связанные с ExportControlViewModel их ViewModel. Представления будут иметь либо ComboBox, либо TextBlock, но не оба (в зависимости от того, может ли пользователь изменить значение).

Ответы [ 5 ]

3 голосов
/ 27 августа 2011

Я не знаю, сработает ли это в вашем случае, но вот возможное решение.В вашей модели представления предоставьте свойство ExportRegulationType, а затем создайте конвертер значений для отображения нужной строки.

Сначала создайте конвертер значений:

class ExportRegulationTypeToStringConverter: IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ExportRegulationType regType = (ExportRegulationType)value;

        switch(regType)
        {
            case ExportRegulationType.EAR:
                return "EAR";
            case ExportRegulationType.DoNotExport:
                return "Do Not Export";

            //handle other cases
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter,    System.Globalization.CultureInfo culture)
    {
        return Binding.DoNothing;
    }

    #endregion
}

Затем добавьтессылка на ваш конвертер в вашем xaml. local - это пространство имен, в котором расположен ваш класс.

<local:ExportRegulationTypeToStringConverter x:Key="exportRegConverter" />

Наконец, установите значение вашего текстового поля для использования конвертера. pathToEnum - это свойство, отображаемое в вашей ViewModel типа ExportRegulationType.

<TextBlock Text="{Binding pathToEnum, Converter={StaticResource exportRegConverter}}" />

Используйте ObjectDataProvider , чтобы заполнить ComboBox значениями перечисления.

<Window.Resources>
 <ObjectDataProvider x:Key="dataFromEnum"
   MethodName="GetValues" ObjectType="{x:Type System:Enum}">
      <ObjectDataProvider.MethodParameters>
           <x:Type TypeName="local:ExportRegulationType"/>
      </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>
</Window.Resources>

Теперь мы создаем ComboBox и используем стиль контейнера с нашим конвертером значений , чтобы отобразить нужные строки для нашего перечисления.

<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}">
    <ComboBox.ItemContainerStyle>
        <Style TargetType="ComboBoxItem">
            <Setter Property="Content" Value="{Binding Converter={StaticResource exportRegConverter}}" />
        </Style>
    </ComboBox.ItemContainerStyle>
</ComboBox>
2 голосов
/ 27 августа 2011

Вместо Dictionary у вас есть другая опция.

См. Следующий вопрос: WPF Привязка ListBox к перечислению с отображением атрибута описания

Youмог бы добавить Description атрибут к вашим перечислениям, как этот

public enum ExportRegulationType
{
    [Description("EAR")]
    EAR,
    [Description("Do Not Export")]
    DoNotExport
}

И когда вы хотите отобразить его, вы можете просто использовать конвертер EnumDescriptionConverter, найденный в вопросе, который я связал

1 голос
/ 27 августа 2011

Я решил это с помощью смеси того, что написали @Dylan и @Meleak. Я ставлю это как ответ, чтобы показать, каким было окончательное решение:

Сначала я реализовал IValueConverter (на основе ответа @ Meleak):

class EnumDescriptionConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Enum regulation = (Enum)value;
        return GetEnumDescription(regulation);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return String.Empty;
    }

    /// <summary>
    /// Returns text intended for display based on the Description Attribute of the enumeration value.
    /// If no Description Attribute is applied, the value is converted to a string and returned.
    /// </summary>
    /// <param name="enumObj">The enumeration value to be converted.</param>
    /// <returns>Text of the Description Attribute or the Enumeration itself converted to string.</returns>
    private string GetEnumDescription(Enum enumObj)
    {
        // Get the DescriptionAttribute of the enum value.
        FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
        object[] attributeArray = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attributeArray.Length == 0)
        {
            // If no Description Attribute was found, default to enum value conversion.
            return enumObj.ToString();
        }
        else
        {
            // Get the text of the Description Attribute
            DescriptionAttribute attrib = attributeArray[0] as DescriptionAttribute;
            return attrib.Description;
        }
    }
}

Я пометил мое перечисление (обратите внимание, что несколько значений не помечены, поскольку желаемый текст совпадает с самим значением):

public enum ExportRegulationType
{
    [Description("Not Determined")]
    NotDetermined,   // Export authority not determined

    EAR,            // Controlled by EAR Regulations

    ITAR,           // Controlled by ITAR Regulations

    [Description("Do Not Export")]
    DoNotExport,    // Export not allowed

    Unrestricted    // Export not controlled
}

В моем App.xaml я объявил ObjectDataProvider для получения списка значений перечисления и EnumDisplayConverter (здесь, поскольку они будут использоваться несколькими различными представлениями):

<Application.Resources>
    [Other stuff...]
    <ObjectDataProvider MethodName="GetValues"
                        ObjectType="{x:Type sys:Enum}"
                        x:Key="ExportRegulationValues">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="models:ExportRegulationType"/>
        </ObjectDataProvider.MethodParameters>      
    </ObjectDataProvider>
    <local:EnumDescriptionConverter x:Key="ExportDisplayConverter"/>
</Application.Resources>

Для текстового блока:

<TextBlock Text="{Binding Export.Regulation, Converter={StaticResource ExportDisplayConverter}}"/>

Для поля со списком:

<ComboBox ItemsSource="{Binding Source={StaticResource ExportRegulationValues}}"
          SelectedValue="{Binding Document.Export.Regulation}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource ExportDisplayConverter}}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Это работает отлично !

1 голос
/ 27 августа 2011

Использовать ObjectDataProvider затем привяжите к нему элементы ComboBox и установите для «DisplayMemberPath» значение «Value».

Что нужно сделать, это показать значения вашего словаря, но в коде за SelectedValue стоит KeyValuePair<>.

Для вашего текстового блока используйте Binding, используя ElementName=yourcombobox и Path=SelectedItem:

<TextBlock Text="{Binding SelectedItem, ElementName=yourcombobox}" />

Дайте мне знать, как это происходит =)

0 голосов
/ 27 августа 2011

Вот мой блог с подходом, использующим прикрепленное поведение.

Он основан на том принципе, что различные значения перечисления не должны ограничиваться переключением строк. Вместо этого вы можете объявить любые части пользовательского интерфейса, которые вы хотите представлять для каждого значения (строки, изображения, различные элементы управления и макеты и т. Д.), И использовать присоединенное поведение для управления их видимостью.

Таким образом, ваша ситуация может быть оформлена как наличие двух разных текстовых блоков, каждый из которых связан с одним и тем же свойством типа ExportRegulationType. Поскольку они связаны с одним и тем же свойством, их видимость взаимоисключающая:

<Grid>
    <TextBlock
        Text="EAR"
        local:EnumVisibility.Value="{Binding ExportRegulationType}"
        local:EnumVisibility.TargetValue="EAR"
    />
    <TextBlock
        Text="Do Not Export"
        local:EnumVisibility.Value="{Binding ExportRegulationType}"
        local:EnumVisibility.TargetValue="DoNotExport"
        FontWeight="Bold"
    />
</Grid>

Я включил FontWeight="Bold", чтобы показать, что вы можете принимать различные решения для каждого значения перечисления. Это также поддерживает локализацию XAML, потому что текст установлен как любой другой текстовый блок.

См. пост для полного ознакомления с решением, примерами кода и zip-файлом, содержащим структуру и пример приложения.

Изменить в ответ на дополнительную информацию:

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

A ComboBox, привязанный к свойству ExportRegulationType, будет выглядеть так:

<ComboBox local:EnumSelector.SelectedValue="{Binding ExportRegulationType, Mode=TwoWay}">
    <ComboBoxItem Content="EAR" local:EnumSelector.ItemValue="EAR" />
    <ComboBoxItem Content="Do Not Export" local:EnumSelector.ItemValue="DoNotExport" />
</ComboBox>

Мы связываем каждый элемент со значением перечисления, а затем используем TwoWay привязку к EnumSelector.SelectedValue, чтобы он записывал обратно в свойство модели представления всякий раз, когда он изменяется.

Это обеспечивает ту же гибкость, что и с текстовыми блоками: вы можете принимать любые решения о том, как установить текст и что содержится в каждом элементе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...