Home > Software engineering >  Binding of ItemsSource property to enum type in code
Binding of ItemsSource property to enum type in code

Time:06-20

I am writing a custom adorner for one of my controls. I want to add a context menu to this adorner and fill one of its items with the values of an enum. I know how to do such a binding in XAML:

<ContextMenu>
    <MenuItem Header="Color" ItemsSource={Binding Source={local:EnumBindingSource {x:Type local:MyColorEnum}, ReturnStrings=True}, Mode=OneTime}/>
</ContextMenu>

where EnumBindingSource is a custom MarkupExtension I wrote for that purpose:

public class EnumBindingSource : MarkupExtension
{
    private Type enumType;
    public Type EnumType
    {
        get => enumType;
        set
        {
            if (enumType != value)
            {
                if (value != null)
                {
                    Type type = Nullable.GetUnderlyingType(value) ?? value;
                    if (!type.IsEnum)
                        throw new ArgumentException("Type must be an enum type");
                }
                enumType = value;
            }
        }
    }

    public bool ReturnStrings { get; set; }

    public EnumBindingSource() { }

    public EnumBindingSource(Type enumType)
    {
        this.enumType = enumType;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        // code to turn enum values into strings, not relevant to the problem
    }
}

In this case, I can't create the binding in XAML because the adorner does not have any XAML code. I want to instead create the binding in C#, which I try to do like this:

internal class MyAdorner : Adorner, INotifyPropertyChanged
{
    (...)
    
    public MyAdorner(UIElement adornedElement) : base(adornedElement)
    {
        (...)
        
        ContextMenu contextMenu = new ContextMenu();
        MenuItem color = new MenuItem()
        {
            Header = "Color"
        };
        color.SetBinding(ItemsControl.ItemsSourceProperty, new Binding()
        {
            Source = new EnumBindingSource(typeof(MyColorEnum))
            {
                ReturnStrings = true
            },
            Mode = BindingMode.OneTime
        });
        contextMenu.Items.Add(color);
        this.ContextMenu = contextMenu;
    }
}

but this fails, it creates nothing. Unfortunately any sources I found online on creating bindings in code do not offer sufficient information for this particular case.

Any help is, as always, greatly appreciated.

CodePudding user response:

When you write this:

ItemsSource="{Binding Source={local:MyMarkup}}"

The xaml engine doesn't directly put the MyMarkup object in the source property.
It calls the MarkupExtension.ProvideValue method with an IProvideValueTarget as argument.
The returned object is the Source.

So the equivalent C# code is:

var ebs = new EnumBindingSource(typeof(MyColorEnum)) { ReturnStrings = true };
color.SetBinding(ItemsControl.ItemsSourceProperty, new Binding()
{
    Source = ebs.ProvideValue(null), // provide a IServiceProvider is hard
    Mode = BindingMode.OneTime
});

By the way, you don't need a binding since the source will not change (MarkupExtension doesn't override INotifyPropertyChanged).
Then you can write:

<ContextMenu>
    <MenuItem Header="Color" ItemsSource="{local:EnumBindingSource {x:Type local:MyColorEnum}, ReturnStrings=True}" />
</ContextMenu>

Working demo of a MRE available here.

  • Related