Introduction to Markup Extension
In XAML, attributes of control are set by assigning a string literal, i.e., a constant value to that attribute. This is hard coding from XAML’s perspective (You can always change the attribute value from the code behind which makes it dynamic).
E.g.
- <Button Content = “Click Me”/>
Sometimes, a developer may want to assign a dynamic value to these attributes which they can extract from other resources like XML, Databases, other controls etc.
E.g.
- <Label Name="lblId"
- Grid.Row="0"
- Grid.Column="0"
- Content="ID"
- HorizontalContentAlignment="Center"
- VerticalContentAlignment="Center"/>
- <TextBox Name="txtId"
- Grid.Row="0"
- Grid.Column="1"
- Text="{Binding ProductId}"
- HorizontalContentAlignment="Center"
- VerticalContentAlignment="Center"/>
In the above example, a developer may want to assign ‘ProductId’ to TextBox after fetching the product object from database at run time. In this case, he/she cannot hard code the text property of the text box at the development time.
Markup Extensions provide this facility to developers. They resolve property values at runtime by executing the code.
The syntax of Markup Extension
{MarkupEntensionClass Argument}
Some of the frequently used markup extensions in WPF are as follows -
- Binding
- StaticResource
- DynamicResource
- TemplateBinding
- X: Static
Binding Enum to ComboBox in WPF
PROBLEMS
ComboBox is an ItemControl. To populate its drop-down list, we need to set its ItemSource property with any IEnumerable object which is a rule of thumb.
Now, Enum type is not IEnumerable which means that we cannot directly assign it to ItemSource property of ComboBox.
SOLUTION
- To populate the ComboBox, we can simply use Enum.GetValues() to get an array of values of the constants in specified Enum. Then we can assign this Array to ItemSource of ComboBox since it is of type IEnumerable in code behind.
CODE BEHIND
- Type enumType = typeof(enumName);
- Array enumArray = Enum.GetValues(enumType);
- cmbBoxName.ItemSource = enumArray;
- The above solution works well, but still, if we have hundreds of Enums in our project, we must write similar code everywhere, which creates a lot of redundant code. Instead, we can create one MarkupExtension which can convert the corresponding Enum to Array or List (which are of type IEnumerable). Then, use this MarkupExtension (which converts Enum to Array or List) in XAML to set ItemSource property to Enum.
CODE - Extend your class with MarkupExtension
- public class BindingEnumToComboBoxExtension : MarkupExtension
- Create private field and property to hold Enum type,
- private Type _enumType;
- public Type EnumType {
- get {
- return _enumType;
- }
- set {
- if (_enumType != _value) {
- if (value != null) {
- Type enumType = Nullable.GetUnderlyingType(value) ? ? value;
- if (!enumType.IsEnum) throw new ArgumentException("Type must be for an Enum.");
- }
- _enumType = value;
- }
- }
- }
- Create Constructors that can be used in XAML to send Enum type to this class,
- public BindingEnumToComboBoxExtension () { }
- public BindingEnumToComboBoxExtension (Type enumType)
- {
- EnumType = enumType;
- }
- Override ProvideValue method of MarkupExtension to convert Enum to Array,
- public override object ProvideValue(IServiceProvider serviceProvider)
- {
- Type actualEnumType = Nullable.GetUnderlyingType(_enumType) ? ? _enumType;
- Array enumValues = Enum.GetValues(actualEnumType);
- if (actualEnumType == _enumType) return enumValues;
- Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1);
- enumValues.CopyTo(tempArray, 1);
- return tempArray;
- }
- Import dll into your project based on where it is. Mine is in the same assembly and Helper folder
- xmlns:local="clr-namespace:BindingEnumToComboBox.Helper"
- Set ItemSource of CombBox.
- ItemsSource="{Binding Source={local:BindingEnumToComboBox {x:Type local:AccessRights}}}"
Output
Let us now look at the output.
Without Markup Extension
With Markup Extension