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


7 comments:

  1. Thank you for your post. We have the same problem right now..., but I have a question. I tried this example on two-way binding and it doesn't work. Please have you any idea how can I resolved it?

    ReplyDelete
    Replies
    1. I tried to play with it. And my conclusion is following.The controls which have two-way binding by default I set on one-way mode before XamlExt.Path property. In the next step I created something like XamlExt.Path but for Mode (XamlExt.Mode) where do I set the correct value for mode.

      {Binding Mode=OneWay, XamlExt.Path=(subjekty:ISomething.Field), XamlExt.Mode=TwoWay}

      This solution is little bit awkward becouse mode is overwritten twice but at present I haven't better idea.
      In any event, this works good and designer not fails.

      P.S: once again thanks for a great idea. :)

      Delete
  2. Hi Richard,

    I had issues with "Mode", it says, when Mode is two-way Path specified required. "XamlExt.Path" and "Path" are different attributes, however XamlExt.Path sets path. That's why I just specify some empty Path first, and after ovewrite it with XamlExt.Path. It's ugly, but it works. Here is the example:

    {Binding Mode=OneWay, Path='.', XamlExt.Path=(subjekty:ISomething.Field)}

    ReplyDelete
  3. I'm planning to update this article. As there were some other issues with PropertyPath parsing, which lead to need of custom TypeConverter for XamlExt.Path. I'll try publish it soon.

    ReplyDelete
  4. Hi Philip,

    I was hoping to use this solution but was wondering if you had fixed this problem with the binding?

    ReplyDelete
    Replies
    1. Hi. Thanks for reminding me about that, I forgot to post updated version. Please see new implementation here: http://blog.pmunin.com/2012/08/xaml-explicit-binding-to-interface.html

      Delete