Привязать textblock к значению словаря для ключа в XAML?

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

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

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

два вопроса:

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

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

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

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

Edit: дополнительная информация:

в приложении у меня будет много разных наборов представлений, моделей и ViewModels. Однако, поскольку правила экспортного контроля общее и последовательное требование, я использую состав для того чтобы держать его сухим. т. е. модели A и B оба имеют модели экспортного контроля. ViewModels A1, A2, B1 и B2 будут иметь ExportControlViewModel. Представления будут иметь элементы управления, привязанные к ExportControlViewModel их ViewModel. Представления будут иметь либо ComboBox, либо TextBlock, но не оба (в зависимости от того, может ли пользователь изменить значение).

5 ответов


Я не знаю, будет ли это работать для вашего случая, но вот возможное решение. В модели представления предоставьте свойство 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:ExportRegulationTypeToStringConverter x:Key="exportRegConverter" />

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

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

использовать ObjectDataProvider чтобы заполнить поле со списком значениями перечисления.

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

вместо Dictionary у вас есть другой вариант.

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

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

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

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


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

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

для вашего textblock используйте Binding используя ElementName=yourcombobox и Path=SelectedItem:

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

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


я решил это со смесью того, что написали @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
}

в моем приложении.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:

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

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


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

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

ваша ситуация, тогда, может быть оформлена как имеющая два разных текстовые блоки, каждый из которых привязан к одному и тому же свойству типа 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 так он напишет назад к свойство модели представления при каждом ее изменении.

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