Modifying GetNamedElementsInScope

Jul 30, 2010 at 3:10 AM

Hi (and of course the ubiquitous thanks for your efforts),

I am trying to use Micro with a WPF application, and the Actipro Ribbon Control. The current version of GetNamedElementsInScope, stops at the top of the ribbon, and does not process the many control collections within the ribbon. In this case, I am focusing on the menus and chrome buttons in particular. They are located in various collections within the ribbon. For example, the application menu is in ribbon.ApplicationMenu.Items, and the footer buttons (see Exit App in Excel 2010) are in ribbon.ApplicationMenu.FooterButtons. While using the XAML based event bindings work, I decided to be a masochist and understand why the auto-binding didn't. Turns out the current GetNamedElementsInScope did not drill into the ribbon, and given the complexity and specific nature of the class, I understand why. However, I decided to try and make it work. After a some hours figuring out the logic used, I came up with the following:

var oldGetNamedElementsInScope = ExtensionMethods.GetNamedElementsInScope;
ExtensionMethods.GetNamedElementsInScope = elementInScope =>
{
  var elements = oldGetNamedElementsInScope(elementInScope);
  var descendants = new List<FrameworkElement>(elements);
  var ribbons = elements.OfType<APR.Ribbon>();

  foreach(var ribbon in ribbons)
  {
    if (ribbon != null)
    {  //  special logic for ribbons
       var appMenu = ribbon.ApplicationMenu;
       if(appMenu != null)
      {
        appMenu.Items.OfType<FrameworkElement>()
           .Where(item => !(item is UserControl) && !string.IsNullOrWhiteSpace(item.Name))
           .Apply(descendants.Add);
        
        if(appMenu.FooterButtons != null)
        {
          appMenu.FooterButtons.OfType<FrameworkElement>()
            .Where(item => !(item is UserControl) && !string.IsNullOrWhiteSpace(item.Name))
            .Apply(descendants.Add);
        }
      }
      
      ribbon.TabPanelItems.OfType<FrameworkElement>()
        .Where(item => !(item is UserControl) && !string.IsNullOrWhiteSpace(item.Name))
        .Apply(descendants.Add);
    }
  }
  return descendants;
};

However, given my ignorance of Micro's internals, I wanted to know if this valid, safe, or even the right way to approach this problem.

Thanks

David

Coordinator
Jul 30, 2010 at 3:53 AM
Edited Jul 30, 2010 at 3:54 AM

Personally, I know nothing about Actipro's ribbon control. But, I made GetNamedElemenstInScope replaceable for exactly this reason. I tried to provide a good general purpose implementation. But, as you say, a Ribbon is a complex control and something I really couldn't find a descent way to handle out-of-the-box. So, your approach is correct. There is another technique you could use as well. If the view you are inspecting is a Window or UserControl itself, you could use reflection to discover all the private fields that inherit from FrameworkElement. Because we know that the compilation process will generate fields for named elements and populate them during initialization. This technique cannot be provided by the framework because it won't work for Silverlight, but you could try it for WPF. You would need to keep the existing implementation for views that don't inherit UserControl or Window, since those would likely be coming from DataTemplates which don't have code-behind-generated fields. However, I'm not sure if this other approach would be any better. It is an alternative option. You would have to do some profiling to see which one performed better. But, you may find that it doesn't really matter that much.

Jul 30, 2010 at 4:01 AM
Cool! I don't think the Actipro ribbon in itself is all that material. I think every ribbon control is going to be a pain due to the number of visual sub-parts, and the unique solutions every vendor seems to take. So, given that this is a valid way to approach this problem, beyond the null checks, what type of error checking or filtering should I apply on this to make sure it doesn't break expectations of the framework? Also, I was wondering why it was only for named controls? Is there some specific reason for this? As soon as I harden this a bit, I will blog about it so someone else gets the benefit of hunting this down.