I have a Listview control in a XAML page which has it's ItemSource bound to a collection in the code behind.
The bound data model within the collection that is bound to each ListView item has one integer field which can contain the value 0 or 1.
I am binding this field to a TextBlock in the ItemTemplate of the ListView, and so in the ListView I am seeing rows containing the text 0 or 1.
My goal is to see "sometext1" instead of 0 and "sometext2" instead of 1 in the ListView without changing the ItemSource itself.
The problems I am seeing are:
TextBlock controls seems to unable to override from code to create a custom TextBlock in which I can change values programatically.
If I use TextBox control instead, I can change the values, but the program slows down when large amount of data is shown and it also not shown the changed values. (In debug mode I can see, that text property has the new value, but the TextBox is empty on screen.)
This is where developing for the Windows platform really is easiest if you do things in the "Windows" way. This involves taking advantage of the awesome functionality built into the XAML presentation framework like binding and converters!
I'm going to ignore that the data comes from a database, as it could come over the network or created programmatically and it shouldn't matter, bottom line is it exists in memory somewhere.
Assuming you have a model class for your data:
class Data
{
public int intField { get; set; }
public string otherField { get; set; }
}
and in the code behind of your page with the listview, an collection of data objects:
private ObservableCollection<Data> ListViewData = new ObservableCollection<Data>();
We can then bind your ListView data to this collection in the XAML page as I'm assuming you have done:
<ListView ItemsSource="{Binding ListViewData}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock x:Name="tbIntField" Text="{Binding intField, Mode=OneWay}"/>
<TextBlock x:Name="tbOtherField" Text="{Binding otherField, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As you have observed, at this point, the contents of the intField TextBlock will be the numeric value of the intField which isn't what you want. So we're going to use a converter to format the TextBlock contents based on the value of the intField.
First, create a new class in your project, I called it intFieldConverter which implements the IValueConverter interface:
class intFieldConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var intField = (int)value;
switch (intField)
{
case 0:
return "Foo";
case 1:
return "Bar";
default:
return default(string);
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
All this does is takes the input int value and returns the string you want to show based on value. I didn't implement the method to convert it back but that's just the same but backwards; you can do that if you need two way binding.
We now need to tell your XAML about this converter. Add your converter namespace to your XAML page if you need it:
<Page
....
xmlns:converters="using:BindingTest.UI.Converter"
....>
We then need to specify the Converter resource in that converters namespace inside the Page tag:
<Page.Resources>
<converters:intFieldConverter x:Key="customIntToStringConverter"/>
</Page.Resources>
We can now use it in our TextBlock binding to convert the values and show the right string in the view:
<TextBox x:Name="tbIntField" Text="{Binding intField, Mode=OneWay, Converter={StaticResource customIntToStringConverter}}"/>
If your codebehind has the observable collection populated with some data, you should now see each row in the listview will now show either "Foo" or "Bar" based upon the value of the intField in the data.
Hope this Helps
Note:
I haven't run any of this code to test it as I don't have a machine to test it on with me at the moment. I can check when I get home to double check if you haven't been able to get it working before.
Related
I have an ObservableCollection in my Model, displayed in a ListBox in my View. Each ListBoxItem displays a radio button which should allow the user to choose one of the items. A property of the ViewModel should record this choice by holding a reference to the chosen item.
How can I set up a two-way binding to do this?
An IValueConverter should allow me to bind RadioButton.IsChecked to the VM property, with the actual item in which the radio button occurs passed to the converter either as a parameter or a value in an IMultiValueConverter. This way I can:
Return true / false for Convert() based on comparison of the VM property and the item the radio button is associated with.
Return the item if IsChecked==true and Binding.DoNothing otherwise for ConvertBack().
However, ValueConverter parameters cannot be bound to because they aren’t dependency properties (so I can't bind to the item to use as a ValueConverter parameter) and I cannot use the MultiConverter because although Convert() will receive both values of interest, ConvertBack() will only receive the value of IsChecked.
Notes
The built in ListBox selection mechanism is already in use for other purposes.
The collection of interest is nested in another collection and presented in a containing ListBox.
The ListBox is bound to a collection in the Model. I am hoping not to implement events in the Model collections that tell the VM how to record events in the View, for obvious reasons.
I managed to solve this:
Include a converter on the resources node of the ItemsPanel which wraps each ListBoxItem (or somewhere else within the repeated part of the tree), so there is one converter instance per item.
Add a property (Host in my example) to the converter for the item each converter attaches to and initialise it somehow* as each ListBoxItem is created.
Bind IsChecked to the ViewModel property you need to have populated using the converter.
*I tried the Host property as a DP with binding to the DataContext (which should be one member of the collection the ListBox is bound to at the point at which the converter is located), but I simply could not get this to work for my nested ListBoxes. I resorted to the Initialized event of the containing element and some code behind (which did allow Host to revert to an ordinary property; the DP was overkill but had been necessary for binding).
The DataTemplate:
<StackPanel Initialized="StackPanel_Initialized">
<StackPanel.Resources>
<!-- ExlcusionRadioConverter.Host is initialised in code behind -->
<local:ExclusionRadioConverter x:Key="ExclusionRadio" />
</StackPanel.Resources>
<RadioButton
GroupName="Exclusion"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}, Converter={StaticResource ExclusionRadio}, Path=DataContext.ExclusionCriterion}" />
<Label Content="{Binding Description}" />
</StackPanel>
The Panel Initialized event (NB this answer originally had this code in the Loaded event handler, but this was causing some problems):
private void StackPanel_Initialized(object sender, EventArgs e)
{
StackPanel panel = (StackPanel)sender;
ExclusionRadioConverter converter = (ExclusionRadioConverter)panel.FindResource("ExclusionRadio");
converter.Host = panel.DataContext as OptionListMember;
}
The Converter:
[ValueConversion(typeof(object), typeof(bool))]
public class ExclusionRadioConverter : IValueConverter
{
public OptionListMember Host { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ReferenceEquals(value, Host);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? Host : Binding.DoNothing;
}
}
I am using C# and .NET 4.5 to create a MVVM desktop application. I have a set of view model entries that are already contained in an ObservableCollection<MyEntryClass>.
I need to use a third-party control to display the data. This control requires that the entries be converted to their own entry classes. They provide an example on how to do that, using XAML and an IValueConverter. It boils down to ...Items="{Binding Path=The.Source, Converter={StaticResource CustomDataConverter}}... and a Converter that is implemented roughly like this:
public class CustomDataConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
IEnumerable<CustomItem> dataContext = value as IEnumerable<CustomItem>;
ObservableCollection<OutputItem> items = new ObservableCollection<OutputItem>();
foreach (CustomItem customItem in dataContext)
{
OutputItemitem =
new OutputItem
{
// ... some value transfers ...
};
items.Add(item);
}
return items;
}
While this works for the initialization of the control, it breaks the "observation chain" between the control and the ObservableCollection specified in the binding -- simply because the custom converter creates its own list of items. Is there a way to change the binding instructions so that the converter is not called once for the entire source collection, but once for every single collection element? If not, what alternative strategies exist for dealing with this kind of situation?
(In the example above I have left out some code that registers an event handler to the source object's PropertyChanged event and updates the target object. Updates of individual item properties are working correctly, it's just the updates of the list that are not handled.)
EDIT: The third party control in question is the GanttChartDataGrid from the Gantt Chart Light Library.
You could try to use the converter in the ItemTemplate, if this third party control offers such possibility. With standard WPF controls it should go something like this:
<ListBox ItemsSource="{Binding Path=The.Source}">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Converter={StaticResource CustomDataConverter}}"
ContentTemplate="{StaticResource MyOptionalDataTemplate}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But maybe the easiest way to achieve this would be to bind a collection that contains OutputItems...
Another approach could be to forget to use converters in XAML code and do your conversions in the ViewModel.
The converter you posted havent a ConvertBack method so I'm guessing the list of ObservableCollection<OutputItem> needed by your thirdy parts control is readonly, meaning no modifications will occur from XAML to C# - as everybody supposes using a Grafic Chart library.
So you can have 2 properties, one private containing your custom object you will update in the ViewModel basing upon your business logic, then one public property binded to your thirdy parts UserControl:
private ObservableCollection<OutputItem> myPrivateData
{
get;
set;
}
private ObservableCollection<CustomItem> DataToPlotOnGant
{
get;
set;
}
Then you can do your conversion stuff inside CollectionChanged event of your private collection:
this.myPrivateData.CollectionChanged += (s, e) =>
{
// do convertion stuff here
};
and choose to update DataToPlotOnGant list by modifying just the item changed in the private collection - more efficiently - or to create every time a new collection like the converter did.
Given a hierarchical data structure of the form:
public class MyData
{
public string Description { get; set; }
public IEnumerable<MyData> SubNodes { get; set; }
}
I would like to display a series of ListBoxes representing that structure. The flow should be left --> right (e.g. like OS X's Finder), where the left-most ListBox contains the root nodes and the right-most the children.
Multiple items in each ListBox should be selectable, causing the available items in subsequent ListBoxes to update. This is trivial to do with a bit of LINQ and a hard-coded number of ListBoxes, however I wanted the template to be dynamic (i.e. ListBoxes should be added and removed depending on the availability of items). I'd also like the solution to be MVVM-compatible.
This type of control might be something that is already bundled in WPF, however I'm not sure what to search for! Any pointers would be appreciated.
First of all, you do not need to use LINQ (or any C# code at all!) to set the ItemSource on the next ListBox. You can use WPF databinding for this:
<ListBox x:Name="listbox1" ItemsSource="{Binding Path=YourDataCollection}"/>
<ListBox x:Name="listbox2" ItemsSource="{Binding ElementName=listbox1, Path=SelectedItem.SubNodes}" />
<ListBox x:Name="listbox3" ItemsSource="{Binding ElementName=listbox2, Path=SelectedItem.SubNodes}" />
Basically you are binding the next listbox to the previous listbox's SelectedItem's SubNodes (you probably should make sure that the listbox can only select one at a time)
Now to hide the listboxes that do not have any items, you could use a IValueConverter to convert the object to a Visibility state. To do this, create a class:
public class ObjectToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And add some more data binding to your listboxes:
<Window.Resources>
<local:ObjectToVisibilityConverter x:Key="objectToVisible" />
</Window.Resources>
<Grid>
<ListBox x:Name="listbox1" ItemsSource="{Binding ElementName=mw1, Path=dataCollection}"/>
<ListBox x:Name="listbox2" ItemsSource="{Binding ElementName=listbox1, Path=SelectedItem.SubNodes}"
Visibility="{Binding ElementName=listbox2, Path=ItemsSource, Converter={StaticResource objectToVisible}}" />
<ListBox x:Name="listbox3" ItemsSource="{Binding ElementName=listbox2, Path=SelectedItem.SubNodes}"
Visibility="{Binding ElementName=listbox3, Path=ItemsSource, Converter={StaticResource objectToVisible}}" />
</Grid>
Make sure to add your namespace to the window, so the valueconverter can actually be found, in my case I added this:
xmlns:local="clr-namespace:WpfApplication1"
Now this solution works only for a fixed amount of listboxes, but it does not rely on any C# code, aside from the valueconverter. Do you really have no idea about how many sublevels you might need? You could use this technique to add dozens of listboxes to a scrollview for example.
To actually make this dynamic in a UserControl might become quite painful as there would be significant amount of code involved to add listboxes, set their items, delete or hide listboxes when a different root node is selected.
Another possibility might be creating a HierarchicalDataTemplate for the TreeView to customize the look of the treeview. This has the advantage of keeping the treeview, as it is the most useful control for hierarchical data, but changing it's looks should be entirely possible with WPF and it's templates. You might need to change the treeview's controltemplate though (I recommend Blend for editing any type of WPF template)
I would like to assign datacontext and static values in a single Combobox. I tried like this,
this.courseList.DataContext = ldc.Courses.OrderBy(x => x.CourseID);
this.courseList.Items.Add("All");
this.courseList.Items.Add("Others");
But, it shows InvalidOperationException was Unhandeled that Items collection must be empty before using ItemsSource.
Then I tried like this,
this.courseList.Items.Add("All");
foreach (var items in ldc.Courses.OrderBy(x => x.CourseID))
{
this.courseList.Items.Add(items);
}
this.courseList.Items.Add("Others");
It works like this Image
Since I used an ItemTemplate in XAML design for this Combobox so it doesn't shows exactly in my way for this two value All and Others.
Here is the ItemTemplate
<DataTemplate x:Key="CourseTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CourseID}"/>
<TextBlock Text=" : "/>
<TextBlock Text="{Binding Path=CourseName}"/>
</StackPanel>
</DataTemplate>
...
<ComboBox Name="courseList" VerticalAlignment="Top" SelectionChanged="courseList_SelectionChanged" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource CourseTemplate}"/>
I want it to view according to DataTemplate for database entries and simple strings All and Others for All and Others respectively like the image below.
Any suggestions. Thank you.
As the error says, you can only use ItemsSource or Items directly, not a mix of the two. Since you essentially have two different types of data that you want to show differently you can add them all to a single bound collection and then let the templates do the work of rendering them differently. Step 1 is to build the single collection:
this.courseList.ItemsSource = new object[] { "All" }.Concat(ldc.Courses.OrderBy(x => x.CourseID)).Concat(new[] { "Others" });
You now have 2 types of data in the collection: String and your object which I'll assume is Course. The strings will show up as you want without any extra work but you need to apply the DataTemplate only to the Course objects. The simplest way is just to replace your x:Key with a DataType:
<DataTemplate DataType="{x:Type data:Course}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CourseID}"/>
<TextBlock Text=" : "/>
<TextBlock Text="{Binding Path=CourseName}"/>
</StackPanel>
</DataTemplate>
where the data xmlns corresponds to your Course object's namespace.
Next just remove the ItemTemplate attribute from your ComboBox and you should get what you're looking for.
If you have other places in the same scope where you're also binding Course objects but want them to display differently you can either scope the DataType template more locally (i.e. a Grid.Resources) or use a DataTemplateSelector on your ComboBox.
You can solve this by using ItemTemplate combined with converter.
Template:
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource ComboBoxItemConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
Converter:
public class ComboBoxItemConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Courses)
{
var course = value as Courses;
return course.CourseID + " : " + course.CourseName;
}
else
{
return value.ToString();
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Since ldc.Courses is set as the DataContext, and you have ItemsSource="{Binding}" this means that the combobox is bound directly to your collection. This creates a problem when you try to add strings as you did since now the collection has both strings and Course objects.
I think this will probably be the easiest solution to understand. Starting with your second approach:
this.courseList.Items.Add("All");
foreach (var items in ldc.Courses.OrderBy(x => x.CourseID))
{
this.courseList.Items.Add(items.CourseID.ToString() + " : " + items.CourseName);
}
this.courseList.Items.Add("Others");
This will keep your combobox bound to a simple list of strings instead of objects, so as long as you don't need other information in the background this will be simpler to digest, if not try one of the other answers that mixes strings and objects into the same collection. This would also mean you don't need to set the DataContext, the ItemsSource, or the DataTemplate, so erase all that.
If you want to keep the template to add a little more control, again you may want to look at some of the other answers. Another alternative would be to create a parent class over your courses object that could also take the more general ALL and OTHER cases and create actual objects around them probably the most flexibility. This way you wouldn't end with a collection of both strings and objects, which will cause you to have to create special code cases anytime you manipulate the collection.
I have a model with an enum property (in this case, related to Export Control Regulations). When displaying the value to the user, I want to show a corresponding string. Sometimes this is in ComboBox (where the user can select a value), and sometimes it is in a TextBlock (where it is read-only).
Example: for ExportRegulationType.EAR, I want to display "EAR", while for ExportRegulationType.DoNotExport, I want to display "Do Not Export". Note that I don't have any language localization needs, but I recognize the issue...
Currently, in my ViewModel, I have a property that returns a string based on the current enum value, and also another property that returns a Dictionary<ExportRegulationType, string>. For the ComboBoxes, I can bind ItemsSource to the dictionary property, and for the TextBlocks, I can bind to the string property. This works, but is kind of clumsy.
Two questions:
1) It seems to me that I should be able to declare the dictionary (with keys and values) as a static resource in XAML (probably in App.xaml), and use that for the ItemsSource for the ComboBox version. However, I can't figure out how to declare and reference such a thing. How can I do that?
2) Assuming the above is in place, I would think I could also set up a binding with the textblock, so based on the enum property, it will look up the string in the dictionary.
I have seen the following questions relating to a static or dynamic enum value. The first isn't adequate, and the second isn't answered...
These should be a XAML-only, and will enable me to remove the methods from my ViewModel (having only the one exposed ExportRegulationType enumerated property. Are these possible?
Edit: Additional information:
In the application, I will have many different sets of views, models, and ViewModels. However, as export control regulations are a common and consistent requirement, I am using composition to keep it DRY. i.e., Models A and B both have an ExportControl model. ViewModels A1, A2, B1 and B2 will have an ExportControlViewModel. The views will have controls bound to the ExportControlViewModel of their ViewModel. The views will have either a ComboBox or a TextBlock, but not both (Depending on if the user can change the value).
I don't know if this will work for your case, but here is a possible solution. In your view model, expose a ExportRegulationType property and then create a value converter to display your desired string.
First create your 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 a reference to your converter in your xaml. local is the namespace in which your class is located.
<local:ExportRegulationTypeToStringConverter x:Key="exportRegConverter" />
Finally, set the value of your text box to use the converter. pathToEnum is the property exposed on your ViewModel of type ExportRegulationType.
<TextBlock Text="{Binding pathToEnum, Converter={StaticResource exportRegConverter}}" />
Use ObjectDataProvider to fill the ComboBox with the values of the enum.
<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 the ComboBox and use a container style with our value converter to display the desired strings for our enum.
<ComboBox ItemsSource="{Binding Source={StaticResource dataFromEnum}}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Content" Value="{Binding Converter={StaticResource exportRegConverter}}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Instead of the Dictionary you have another option.
See the following question: WPF Binding a ListBox to an enum, displaying the Description Attribute
You could add a Description Attribute to your enums like this
public enum ExportRegulationType
{
[Description("EAR")]
EAR,
[Description("Do Not Export")]
DoNotExport
}
And when you want to display it, you can just use EnumDescriptionConverter Converter found in the question I linked
I solved this with a blend of what #Dylan and #Meleak wrote. I'm putting this as an answer to show what the final solution was:
First, I implemented an IValueConverter, (based on #Meleak's 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 tagged my enum (note that several values are not tagged as the desired text is the same as 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 the ObjectDataProvider to get the list of enum values and the EnumDisplayConverter (Here since they will be used by several different views):
<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 a TextBlock:
<TextBlock Text="{Binding Export.Regulation, Converter={StaticResource ExportDisplayConverter}}"/>
For a 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>
This works perfectly!
Use an ObjectDataProvider
then bind the ComboBox's Items to it, and set "DisplayMemberPath" to "Value".
What this should do is to show the values of your dictionary, but in code-behind the SelectedValue is a KeyValuePair<>.
For your textblock, use a Binding using ElementName=yourcombobox and Path=SelectedItem:
<TextBlock Text="{Binding SelectedItem, ElementName=yourcombobox}" />
Let me know how it goes =)
Here is a blog post of mine with an approach using attached behaviors.
It is based on the principle that different enumeration values don't need to be limited to switching strings. Instead, you can declare whatever pieces of UI you want to represent each value (strings, images, different controls and layouts, etc.) and use an attached behavior to control their visibility.
Your situation, then, can be framed as having two different text blocks, each bound to the same property of type ExportRegulationType. Since they are bound to the same property, their visibilities are 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 the FontWeight="Bold" to show that you can make different decisions for each enumeration value. This also supports XAML localization because the text is set like any other text block.
See the post for a complete walkthrough of the solution, code samples, and a zip file containing the framework and an example application.
Edit in response to additional information:
Here is another post in the same series which describes how to select enumeration values with Selector controls.
A ComboBox bound to the ExportRegulationType property would look this 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 associate each item with an enumeration value, then use a TwoWay binding to EnumSelector.SelectedValue so it will write back to the view model's property whenever it changes.
This provides the same flexibility as with the text blocks: you can make whatever decisions you want about how to set the text and what is contained by each item.