Associate a text block with a dictionary value for a key in XAML?

I have a model with the enum property (in this case, associated with the Export Control Rules). When displaying the value to the user, I want to show the corresponding line. Sometimes this happens in ComboBox (where the user can select a value), and sometimes in TextBlock (where it is read-only).

Example: for ExportRegulationType.EAR I want to display "EAR" , and for ExportRegulationType.EAR I want to display "Do Not Export" . Please note that I have no language localization requirements, but I understand the problem ...

Currently in my ViewModel, I have a property that returns a string based on the current value of the enumeration, as well as another property that returns Dictionary<ExportRegulationType, string> . For ComboBoxes, I can bind ItemsSource to a dictionary property, and for TextBlocks I can bind to a string property. It works, but rather clumsy.

Two questions:

1) It seems to me that I should declare the dictionary (with keys and values) as a static resource in XAML (possibly in App.xaml) and use it for the ItemsSource version for ComboBox. However, I cannot understand how to declare and refer to such a thing. How can i do this?

2) Assuming that this was done, I think that I could also set the binding to the text block, so, based on the enum property, it will look for a string in the dictionary.

I saw the following questions related to static or dynamic enumeration value. The first is not adequate, and the second does not respond ...

It should be only XAML, and it will allow me to remove methods from my ViewModel (having only one ExportRegulationType property ExportRegulationType . Is this possible?

Edit: Additional information:

In the application, I will have many different sets of views, models, and ViewModels. However, since export control standards are a general and consistent requirement, I use the composition to keep it DRY. that is, models A and B have an ExportControl model. ViewModels A1, A2, B1 and B2 will have an ExportControlViewModel. Views will have controls associated with the ExportControlViewModel of their ViewModel. Views will have either a ComboBox or TextBlock, but not both (depending on whether the user can change the value).

+6
source share
5 answers

I do not know if this will work for your business, but here is a possible solution. In your view model, derive the ExportRegulationType property, and then create a value converter to display the row you want.

First create a value converter:

 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 } 

Then add the link to your converter in your xaml. local is the namespace your class is in.

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

Finally, set the value of your text box to use the converter. pathToEnum is a property set on your ViewModel of type ExportRegulationType.

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

Use ObjectDataProvider to populate the ComboBox with enumeration values.

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

Now we create a ComboBox and use the container style with our value converter to display the necessary lines for our enumeration.

 <ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}"> <ComboBox.ItemContainerStyle> <Style TargetType="ComboBoxItem"> <Setter Property="Content" Value="{Binding Converter={StaticResource exportRegConverter}}" /> </Style> </ComboBox.ItemContainerStyle> </ComboBox> 
+3
source

Instead of Dictionary you have one more option.

See the following question: WPF Linking a ListBox to an Enumeration Displaying a Description Attribute

You can add the Description attribute to your listings as follows

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

And when you want to display it, you can simply use the EnumDescriptionConverter Converter found in the question I linked

+2
source

Use an ObjectDataProvider then bind the ComboBox elements to it and set the DisplayMemberPath parameter to Value.

What you need to do is to show the values โ€‹โ€‹of your dictionary, but the code <<20> has KeyValuePair<> .

For your text block, use Binding with ElementName=yourcombobox and Path=SelectedItem :

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

Let me know how it goes =)

+1
source

I solved this with what @Dylan and @Meleak wrote. I put this as an answer to show what the final solution was:

Firstly, I implemented IValueConverter (based on @Meleak answer):

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

I marked my listing (note that several values โ€‹โ€‹are not marked as the desired text matches the value itself):

 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 } 

In my App.xaml, I declared ObjectDataProvider to get a list of enum values โ€‹โ€‹and EnumDisplayConverter (here, since they will be used in several different ways):

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

For TextBlock:

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

For combo box:

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

It works great!

+1
source

Here is my blog post with an approach using the attached behavior.

It is based on the principle that various enumeration values โ€‹โ€‹should not be limited to line switching. Instead, you can declare all parts of the user interface that you want to represent each value (lines, images, various controls and layouts, etc.) and use the attached behavior to control their visibility.

Thus, your situation can be framed as having two different text blocks, each of which is tied to the same property of type ExportRegulationType . Since they are associated with the same property, their visibility is mutually exclusive:

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

I included FontWeight="Bold" to show that you can make different decisions for each value of the enumeration. It also supports XAML localization, because the text is specified like any other text block.

See the post for a complete overview of the solution, sample code, and a zip file containing the framework and sample application.

Change in response to additional information:

Here is another publication in the same series that describes how to select enumeration values โ€‹โ€‹using Selector controls.

A ComboBox associated with the ExportRegulationType property will look like this:

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

We map each element to an enumeration value, and then bind TwoWay to EnumSelector.SelectedValue so that it returns a view model property whenever it changes.

This provides the same flexibility as with text blocks: you can make any decisions you want about how to set the text and what each element contains.

0
source

Source: https://habr.com/ru/post/896002/


All Articles