WPF ComboBox Enum automatic Binding and Localization

Use it like this:


<ComboBox
behaviors:ComboBoxEnumBehavior.EnumType="{x:Type ClickMode}"
behaviors:ComboBoxEnumBehavior.ResourceManager="{Binding Source={x:Static resources:MyResources.ResourceManager}}" />

And here is the code:


public static class ComboBoxEnumBehavior

public static ResourceManager GetResourceManager(DependencyObject obj)
{
return (ResourceManager)obj.GetValue(ResourceManagerProperty);
}

public static void SetResourceManager(DependencyObject obj, ResourceManager value)
{
obj.SetValue(ResourceManagerProperty, value);
}

public static readonly DependencyProperty ResourceManagerProperty =
DependencyProperty.RegisterAttached(“ResourceManager”, typeof(ResourceManager), typeof(ComboBoxEnumBehavior), new PropertyMetadata(null, OnChanged));

private static ObservableCollection GetEnumNames(DependencyObject obj)
{
return (ObservableCollection)obj.GetValue(EnumNamesProperty);
}

private static void SetEnumNames(DependencyObject obj, ObservableCollection value)
{
obj.SetValue(EnumNamesPropertyKey, value);
}

private static readonly DependencyPropertyKey EnumNamesPropertyKey =
DependencyProperty.RegisterAttachedReadOnly(“EnumNames”, typeof(ObservableCollection), typeof(ComboBoxEnumBehavior), new PropertyMetadata(null));

private static readonly DependencyProperty EnumNamesProperty = EnumNamesPropertyKey.DependencyProperty;

public static Type GetEnumType(DependencyObject obj)
{
return (Type)obj.GetValue(EnumTypeProperty);
}

public static void SetEnumType(DependencyObject obj, Type value)
{
obj.SetValue(EnumTypeProperty, value);
}

public static readonly DependencyProperty EnumTypeProperty =
DependencyProperty.RegisterAttached(“EnumType”, typeof(Type), typeof(ComboBoxEnumBehavior), new PropertyMetadata(null, OnChanged));

private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ComboBox comboBox = d as ComboBox;
Type enumType = GetEnumType(comboBox);
ResourceManager resourceManager = GetResourceManager(comboBox);
if (comboBox != null && enumType != null)
{
UpdateItemsSourceBinding(comboBox, enumType, resourceManager);
}
}

private static void UpdateItemsSourceBinding(ComboBox comboBox, Type enumType, ResourceManager resourceManager)
{
if (!enumType.IsEnum)
{
throw new ArgumentException(“EnumType must be an System.Enum”);
}
if (enumType != null && comboBox != null)
{
// Fill EnumNames attached property
ObservableCollection enumNames = new ObservableCollection();
foreach (var item in Enum.GetNames(enumType))
{
if (resourceManager != null)
{
// with localization
var key = enumType.FullName.Replace(“.”, “_”) + “_” + item;
var localizedString = resourceManager.GetString(key);
if (string.IsNullOrEmpty(localizedString))
{
enumNames.Add(“??? ” + resourceManager.BaseName + “.” + key);
}
else
{
enumNames.Add(localizedString);
}
}
else
{
// without localization
enumNames.Add(item);
}
}
comboBox.SetValue(ComboBoxEnumBehavior.EnumNamesPropertyKey, enumNames);

// Add Binding for ItemsSource
Binding enumNamesBinding = new Binding();
enumNamesBinding.Path = new PropertyPath(“(0)”, ComboBoxEnumBehavior.EnumNamesProperty);
enumNamesBinding.RelativeSource = new RelativeSource(RelativeSourceMode.Self);
BindingOperations.SetBinding(comboBox, ComboBox.ItemsSourceProperty, enumNamesBinding);

comboBox.IsSynchronizedWithCurrentItem = true;

// Add Binding for SelectedItem
Binding selectedItemBinding = new Binding(“Selected” + enumType.Name);
selectedItemBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
selectedItemBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(comboBox, ComboBox.SelectedItemProperty, selectedItemBinding);
}

}

}

 

Of course you could turn this into a “real” behavior but I wanted to keep it simple for the post.
Don’t forget the xmlns:behaviors=”clr-namespace:Your.Namespace;assembly=Your.Assemby” in your page / window.
You will need xmlns:resources=”clr-namespace:blabla” for the location of you .RESX as well.
Code will only work if you set “Access Modifier” to “Public” for your .resx file.
(just insert PublicResXFileCodeGenerator as Custom Tool in .resx properties)

Multiple Inheritance

I you are developing a somewhat bigger project, sooner or later you will find yourself in a situation when you have already wasted your one and only allowed base class and say “It would be nice if this class could also derive from another one”. Well it wouldn’t. But that is a different story. Still, the problem can be solved in different ways:

Of course just the first one is meant seriously.

Let’s first take a look at why we want inheritance in the first place:

Two reasons: Polymorphism and Maintainability.

In my case the focus lies on maintainability. We do not want to have duplicate code. Or to be more specific, no duplicate code that is handwritten and therefore has an impact on maintainability. T4 templates to the rescue! http://msdn.microsoft.com/de-de/library/ee844259(v=vs.100).aspx 

I have some interfaces that many of my classes (ViewModels in my case) have to implement, and those ViewModels already have a base class. The interfaces are used by central behavior and manager classes that help organizing and unifying the user interface. The implementation of those interfaces is always the same, most of them just define some properties and the implementation takes care of the INotifyPropertyChanged.PropertyChanged event being raised properly.

Here is an example of such an interface:

IExecutableItem.cs

   public interface IExecutableItem
   {
        public ICommand Command { get; set; }
        public object CommandParameter { get; set; }
        public event CommandExecutedEventHandler Executed;
   }

Now with the help of T4 templates you can do this:

1.) Your create a partial class for your ViewModel that contains your handwritten code.

MyViewModel.cs

    public partial class MyViewModel : ViewModelBase
    {
        public MyViewModel(IServiceLocator serviceLocator)
            : base(serviceLocator)
        {  }
        // [ more code ... ]
    }

2.) You create a T4 Template File that generates the implementation of additional interfaces:

MyViewModel.ModelProperties.tt

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ include file="..\ModelProperties\ModelProperties.tt" #>
<#
	Implement(
		@"..\ModelProperties\",
		"ISelectableItem;IExecutableItem;IOrderedItem"
	);
#>

3.) You are done.

To make this work you need the included file ModelProperties.tt and one .tt file per interface you wish to be implementable in this way. I will give you the interface .tt for the IExecutableItem interface as an example:

IExecutableItem.tt

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ parameter name="ClassName" type="System.String" #>
<#@ parameter name="Namespace" type="System.String" #>
<#
	var usings = (List<string>)CallContext.GetData("usings");
	usings.Add("using System.Windows.Input;");
	usings.Add("using MyCompany.MyProject.Tools;");
#>
namespace <#=Namespace#>
{

    public partial class <#=ClassName#> : IExecutableItem
    {

        private ICommand _Command;

        /// <summary>
        /// Command
        /// </summary>
        public ICommand Command
        {
            get { return _Command; }
            set
            {
                if (_Command != value)
                {
                    _Command = value;
                    base.RaisePropertyChanged(() => Command);
                }
            }
        }

        private object _CommandParameter;

        /// <summary>
        /// CommandParameter
        /// </summary>
        public object CommandParameter
        {
            get { return _CommandParameter; }
            set
            {
                if (_CommandParameter != value)
                {
                    _CommandParameter = value;
                    base.RaisePropertyChanged(() => CommandParameter);
                }
            }
        }

        private event CommandExecutedEventHandler _Executed;

        /// <summary>
        /// Executed
        /// </summary>
        public event CommandExecutedEventHandler Executed
        {
            add { _Executed += value; }
            remove { _Executed -= value; }
        }

    }

}

and here is the ModelProperties.tt:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name = "System.Core" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#+

		public void Implement(string path, string interfaces)
		{
			TextTemplatingSession session = new TextTemplatingSession();
			session["Namespace"] = "MyCompany.MyProject.Demo";
			session["ClassName"] = "MyViewModel";
			var usings = new List<string>();
			CallContext.SetData("usings", usings);
			var sessionHost = (ITextTemplatingSessionHost) this.Host;
			sessionHost.Session = session;
			foreach (var item in interfaces.Split(';'))
			{
				GenerationEnvironment.Append(ProcessTemplate(Path.Combine(path, item.Trim() + ".tt")));
			}
			this.GenerationEnvironment.Insert(0, Environment.NewLine);
			foreach (var item in usings.Distinct().OrderByDescending(x => x))
			{
				this.GenerationEnvironment.Insert(0, item + Environment.NewLine);
			}
		}

		public string ProcessTemplate(string templateFileName)
		{
			string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
			string template = File.ReadAllText(Host.ResolvePath(templateFileName));
			Engine engine = new Engine();
			string output = engine.ProcessTemplate(template, Host);
			return output + Environment.NewLine;
		}

#>

The Implement method creates a session and stores the classname and the namespace in it. It also stores a stringlist in the CallContext to allow the interface .tt’s to add usings to the output. Then each interface .tt is executed and the output is added. Finally the content of the using stringlist is made distinct, sorted and inserted at the top of the generated file.

You end up with a ViewModel that has all the implementations from your interface .tt files. I know this is not “real” inheritance but it serves our main goal: Maintainability. Changes in the interface .tt files will be reflected in all “derived” classes once you hit “Transform all templates” again.

You could also combine this idea with the aforementioned “Simulated Multiple Inheritance Pattern” and make your templates generate all the redirecting methods. That way you would end up being able to also extend classes instead of just interfaces. I’ll leave this to you as a an exercise 😉

This is still not an out-of-the-box solution because you have to make all classes and interfaces “implementable” but usually you have only some base classes and interfaces and many many classes deriving from them.