Monday, February 20, 2012

XAML Explicit binding to Interface - Object 'null' cannot be used as an accessor parameter for a PropertyPath


Object 'null' cannot be used as an accessor parameter for a PropertyPath. An accessor parameter must be DependencyProperty, PropertyInfo, or PropertyDescriptor.

Note: Implementation of this post is obsolete. Please see new version here: http://blog.pmunin.com/2012/08/xaml-explicit-binding-to-interface.html


Everyone who works with MVVM pattern in WPF had met this problem with data binding to interface property. When you use bindin with explicit PropertyPath (like Path=(local:IMyInterface.MyProperty)) your designer is no longer working (neither Visual Studio XAML, nor Expression Blend).

The interesting/weird thing is that it screw up only if you use this binding in visual tree of your user control or windows, but not in DataTemplate/HierarchicalDataTemplate. Explicit binding inside DataTemplate works fine for me.


Finally I've found some workaround solution that allows you to use binding to interfaces' properties without breaking you Visual Studio and Expression Blend designers in main visual tree. But you will have to add something to your XAML. Basically instead of writing  Path=(local:IMyInterface.MyProperty), you will have to write  XamlExt.Path=(local:IMyInterface.MyProperty).

This is how you can achieve it:
1) Create Attached Property to Binding class.
2) Register Xml namespace mapping.
3) Enjoy full MVVM pattern in your Xaml applications with working designer

Here is code of XAML Markup Extension in separate class library:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Data;
using System.Diagnostics;
using System.ComponentModel;
 
namespace MyWPFApplication.Extensions
{
    /// 
    /// Binding extensions required to bind to interface property 
    /// without breaking designer
    /// 
    public static partial class XamlExt
    {
 
 
        public static object GetPath(System.Windows.Data.Binding obj)
        {
            return obj.Path;
        }
        public static void SetPath(System.Windows.Data.Binding obj, object value)
        {
 
            try
            {
                obj.Path = (PropertyPath)value;
            }
            catch (Exception e)
            {
                PresentationTraceSources
                   .DataBindingSource
                   .TraceData(TraceEventType.Error, 0, e);
                throw;
            }
        }
 
        public static readonly DependencyProperty PathProperty =
            DependencyProperty.RegisterAttached("Path"
                        typeof(PropertyPath)
typeof(XamlExt)
new PropertyMetadata());
 
    }
 
}


In order to use it conveniently in you XAML, you just need to add attribute in Assembly.cs:

[assemblyXmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation"
"MyWPFApplication.Extensions")]



Now add modification to your old code.
Old code:

<Button
   x:Name="btnAdd"
   Content="Add"
   Command="{Binding Path=(local:IViewModel.AddCommand)}" />


New code:


<Button
   x:Name="btnAdd"
   Content="Add"
   Command="{Binding XamlExt.Path=(local:IViewModel.AddCommand)}" />




Note: Implementation of this post is obsolete. Please see new version here: http://blog.pmunin.com/2012/08/xaml-explicit-binding-to-interface.html