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)