Adding Infragistics Elements to the ViewModelBinder

Feb 14, 2011 at 11:24 PM

I'm trying to use the Infragistics WPF Ribbon along with the Caliburn Micro framework.

The Infragistics Ribbon (which includes Infragistic Ribbon controls based on standard WPF controls like ButtonBase) unfortunately is not a ContentControl and so the default Caliburn framework fails to add any of the ribbon buttons to the ViewModelBinder's list of valid GUI elements.  Furthermore, the Infragistics Ribbon does not return any child controls when using the "VisualTreeHelper" on it.

The existing Caliburn code relies on using the detection of ContentControls as well as the VisualTreeHelper to locate and identify valid FrameworkElements.

The Caliburn documentation says that one can customize the searching of elements by providing a new delegate for GetNamedElementsInScope. This func has actually been changed to BindingScope.GetNamedElements.  The documentation goes on to say:

If the view is a UserControl or Window, instead of walking the tree for elements, use some reflection to discover all private fields that inherit from FrameworkElement.

We know that when Xaml files are compiled, a private field is created for everything with an x:Name. Use this to your advantage.

I have spent a few hours trying to figure out how to do this, but am new to WPF and not too familiar with reflection.  My current code is: 

        BindingScope.GetNamedElements = elementInScope => {

          //your logic here
          IEnumerable<FrameworkElement> result = ?

            return result;
        }

I have gone through my WPF xaml and added x.Name properties to all the Infragistics and non-Infragistics elements I want to hook to events or databind in the view model.  I just don't know how to search through assemblies to find all the frameworkelements with names and return them to the GetNamedElements func.

Any assistance or suggestions are greatly appreciated!

Feb 14, 2011 at 11:40 PM

By reading some of the other posts, I found a solution that works.  Re-posting here for others who may run into this issue:

 

    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;
    }

Jul 22, 2011 at 9:34 AM

Hi,

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 ...

 

Regards,

Alek Stankiewicz