Silverlight Toolkit ContextMenu/MenuItem

Jul 21, 2010 at 11:50 AM

Hi there,

As I understand, there is no automatic binding support for ContextMenu from Silverlight Toolkit.

Any clues, where and what to "tune" to support MenuItem binding?

Thank you in advance,


Jul 21, 2010 at 1:44 PM
Edited Jul 21, 2010 at 1:49 PM

I am not entirely familiar with the ContextMenu control but the idea is that CM supports some "obvious" bindings.

You can add your own custom bindings via the ConventionManager class: 

Here is an example where I added a convention for a Progress Bar:

ConventionManager.AddElementConvention<ProgressBar>(RangeBase.ValueProperty, "Value", "Loaded");

Put that in your bootstrapper's constructor.


Jul 21, 2010 at 2:33 PM

Unfortunately  ContextMenu is complicated. pompomJuice is correct in that you need to add a convention to the ConventionManager. However, the real problem is that ContextManager does not live in the VisualTree. So, we have no generic way of finding it when we are searching for conventions. So, you would additionally have to replace ExtensionMethods.GetNamedElementsInScope with a new implementation that checked every element (or you could limit it to elements of a certain type) for the existance of the attached property, then if found, iterate its menu items and return those that are named.

There is another possible implementation which might work (perhaps I should test it and make it the default implementation if it does...or you could just let me know) Here's how it might work:

Replace the ExtensionMethods.GetNamedElementsInScope with a new implementation that adds additional behavior. First it would check to see if the DependencyObject passed in was a Window or UserControl. If so, it could use reflection to get all private fields on the immediate class whose field type are DependencyObjects. That should get you all named elements, regardless of whether they are  in the VisualTree or not. But, this won't work for DataTemplates that aren't UserControls, so you would have to fall back to the existing VisualTree search for those scenarios. Does that make sense?

Jul 21, 2010 at 2:36 PM

To be clear, the second idea is based on the fact that at compile time, the WPF/SL framework generates fields for all named elements and when the initialization occurs, it attaches them to their control instances. So, it should work in theory, but I haven't tried it yet myself. I'm inclined to think that if it works, it should be faster than walking the visual tree. Let me know if you try this and if it works for the ContextMenu sample. If you are having trouble, send me a simple solution and I will work with you on this. 

Jul 21, 2010 at 3:38 PM

Before I'll try to do it at home :) Could Silverlight reflect private members?

Jul 21, 2010 at 4:01 PM

Using Silverlight, you can get the private members, but you cannot get or set their values. For our purposes, that should be enough. I think that's the way it works...

Jul 21, 2010 at 4:15 PM

I just tried to use cal:Message.Attach, both my Can-methods and Perform-methods are getting called on popup :)

That is weird

Jul 21, 2010 at 4:18 PM

MenuItem is derived from HeaderedItemsControl, which is derived from ItemsControl... 

So probably default message invocation works via Loaded event...

Jul 21, 2010 at 4:30 PM

Ok, I've managed to get it work. But with cal:Message.Attach only.

We just need to register additional convention like this one:

AddElementConvention<MenuItem>(MenuItem.ItemsSourceProperty, "DataContext", "Click");

Jul 21, 2010 at 6:19 PM

So here is alternative solution which works as Mr. Eisenberg described above:

    static RootBootstrapper()
      ExtensionMethods.GetNamedElementsInScope = GetFields;

    static Dictionary<Type, List<FieldInfo>> _typeCache = new Dictionary<Type, List<FieldInfo>>();

    static IEnumerable<FrameworkElement> GetFields(DependencyObject obj)
      var fields = GetFields(obj.GetType());
      if (fields != null)
        foreach (var f in fields)
          var result = f.GetValue(obj) as FrameworkElement;
          if (result != null)
            yield return result;

    static IEnumerable<FieldInfo> GetFields(Type type)
      List<FieldInfo> result;

      if (_typeCache.TryGetValue(type, out result))
        return result;

      result = type.
        GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).
        Where(i => typeof(FrameworkElement).IsAssignableFrom(i.FieldType)).ToList();

      if (result.Count == 0)
        result = null;

      _typeCache[type] = result;

      return result;

Since binding takes place in main thread, I decided not to thread-safe lock the _typeCache access...

Jul 21, 2010 at 6:30 PM

Hmm. Now that I am looking at doesn't seam like it would work in Silverlight because of the GetValue call on the field. Did you get this to work?

Jul 22, 2010 at 11:43 AM

Probably because RootBootstrapper is defined in the same assembly where all my views are ;)

Jul 22, 2011 at 9:55 AM
Edited Jul 22, 2011 at 9:56 AM


when working with MEF and multiple assemblies/XAPs you should give access your assembly with new conventions to "external" assemblies like below (put it in AssemblyInfo.cs of your "external"assembly project):

[assembly: InternalsVisibleTo("AssemblyWithNewConventionCode")]

Without such attribute your code will not take the value of objects property ...

Alek Stankiewicz