Tuesday, August 28, 2012

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

This is evolution of my previous post: http://blog.pmunin.com/2012/02/xaml-explicit-binding-to-interface.html
I've decided not to overwrite the old one, but create new post instead.

So eventually I decided to resolve issue using separate binding class, which is inherited from Binding, but contains all necessary improvements.

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;
[assemblyXmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation""PMunin.com")]
namespace PMunin.com
{
    /// 
    /// Inherited from Binding, have some improvements:    ///  - does not crash VS2010 WPF designer if Path contains Interface.    ///  - ValidateOnDataErrors and ValidateOnExceptions are true by default.    /// 

    public class XamlExtBinding : Binding
    {
        public XamlExtBinding():base()
        {
            NotifyOnValidationError = true;
            ValidatesOnDataErrors = true;
            ValidatesOnExceptions = true;
        }
        public XamlExtBinding(string path):base(path)
        {
            NotifyOnValidationError = true;
            ValidatesOnDataErrors = true;
            ValidatesOnExceptions = true;
        }
        [TypeConverter(typeof(ExtPropertyPathConverter))]
        public new object Path
        {
            get
            {
                return base.Path;
            }
            set
            {
                base.Path = value as PropertyPath;
            }
        }
        /// 
        /// Required to fix bug "Prefix 'local' does not map to a namespace"        /// when Path with namespace used in DataTemplate like here (http://devexpress.com/Support/Center/p/Q352753.aspx)        /// 
        public class ExtPropertyPathConverter : TypeConverter        {
            static PropertyPathConverter pathConverter = new PropertyPathConverter();
            static bool IsInDesignMode(ITypeDescriptorContext context)
            {
                bool isInDesignMode = DesignerProperties.GetIsInDesignMode(Application.Current.MainWindow);
                return isInDesignMode;
            }
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                bool res = true;
                try                {
                    res = pathConverter.CanConvertFrom(context, sourceType);
                }
                catch                {
                    if (!IsInDesignMode(context)) throw;
                }
                return res;
            }
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                var res = true;
                try                {
                    res = pathConverter.CanConvertTo(context, destinationType);
                }
                catch                {
                    if (!IsInDesignMode(context)) throw;
                }
                return res;
            }
            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                object res = null;
                try                {
                    res = pathConverter.ConvertFrom(context, culture, value);
                }
                catch                {
                    if (!IsInDesignMode(context)) throw;
                }
                return res;
            }
            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                object res = null;
                try                {
                    res = pathConverter.ConvertTo(context, culture, value, destinationType);
                }
                catch                {
                    if (!IsInDesignMode(context)) throw;
                }
                return res;
            }
        }
    }
}


You should put this class in separate shared class library, that is referenced by you project with XAML files you need to edit. This will allow you to use in very simple way you get used to - anywhere you have {Binding ....}, or just replace it with {XamlExtBinding ...} or . No namespace declaration required.
This code was written long time ago, so I'm posting it now, but I'm not sure that included everything. Please feel free to comment - if you face any issues.