1

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.

3
  • You should reformulate your question to simplify it, the fact that it is an adorner or a context menu or that you have a really complicated MarkupExtension derived class as nothing to do with you issue. Commented Jun 17, 2022 at 14:56
  • Does this answer your question? Set custom MarkupExtension from code Commented Jun 17, 2022 at 15:15
  • @Orace wanted to be precise and complete so people won't have to wonder what I'm doing or ask for code X but I agree the ProvideValue method is not relevant. I was on the fence about adding it to the question. I edited it out now. Commented Jun 20, 2022 at 8:13

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, this works. Didn't realize simply passing null for an IServiceProvider would work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.