tags:

views:

355

answers:

4

I'm trying to bind controls in a WPF form to an interface and I get a runtime error that it can't find the interface's properties.

Here's the class I'm using as a datasource:

public interface IPerson
{
    string UserId { get; set; }
    string UserName { get; set; }
    string Email { get; set; }
}

public class Person : EntityBase, IPerson
{
    public virtual string UserId { get; set; }
    public string UserName { get; set; }
    public virtual string Email { get; set; }
}

Here's the XAML (an excerpt):

<TextBox Name="userIdTextBox" Text="{Binding UserId}" />
<TextBox Name="userNameTextBox" Text="{Binding UserName}" />
<TextBox Name="emailTextBox" Text="{Binding Email}" />

Here's the code behind (again, an excerpt):

var person = PolicyInjection.Wrap<IPerson>(new Person());
person.UserId = "jdoe";
person.UserName = "John Doe";
person.Email = "[email protected]";

this.DataContext = person;

Note that the class I'm using as the data source needs to be an entity because I'm using Policy Injection through the entlib's Policy Injection Application Block.

I'm getting this error at runtime:

System.Windows.Data Error: 16 : Cannot get 'Email' value (type 'String') from '' (type 'Person'). BindingExpression:Path=Email; DataItem='Person' (HashCode=22322349); target element is 'TextBox' (Name='emailTextBox'); target property is 'Text' (type 'String') TargetException:'System.Reflection.TargetException: Object does not match target type.
   at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
   at MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
   at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'
A: 

I've run a contrived example which does NOT use the Policy Injection Application Block, and I do not run into any problems with data binding. However, here is an IValueConverter you can use to inspect the types being passed to the TextBox from those paths:

public class DebugBindingPathConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Console.WriteLine("value = {0}", value);
        Console.WriteLine("value.GetType() = {0}", value != null ? value.GetType().FullName : "<null>");
        Console.WriteLine("targetType = {0}", targetType.FullName);
        Console.WriteLine("parameter = {0}", parameter);
        Console.WriteLine("parameter.GetType() = {0}", parameter != null ? parameter.GetType().FullName : "<null>");
        Console.WriteLine("culture = {0}", culture);

        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Used in the XAML like so:

<UserControl.Resources>
    <local:DebugBindingPathConverter x:Key="debugBinding" />
</UserControl.Resources>
<StackPanel>
    <TextBox Name="userIdTextBox" Text="{Binding UserId, Converter={StaticResource debugBinding}}" />
    <TextBox Name="userNameTextBox" Text="{Binding UserName, Converter={StaticResource debugBinding}}" />
    <TextBox Name="emailTextBox" Text="{Binding Email, Converter={StaticResource debugBinding}}" />
</StackPanel>

So set a breakpoint inside of the Convert routine and you should be able to start debugging this problem.

sixlettervariables
I added this value converter and my breakpoint in the Convert routine wasn't hit. I'm not familiar with how converters work, but there was probably something simple missed.
Matt Casto
+2  A: 

I'm not familiar with entlib's policy injection, but I'm pretty sure that your problem lies there, and not in the fact that you're using an interface.
If you were to replace

var person = PolicyInjection.Wrap<IPerson>(new Person());

with

IPerson person = new Person();

surely it would work?

Robert Jeppesen
@Robert Jeppesen: that doesn't allow his PolicyInjection code to run.
sixlettervariables
Robert is correct - the problem is that WPF data binding doesn't recognize the properties in the proxy class that PolicyInjection.Wrap creates. Data binding works fine without PI, so I just need to come up with a work around that doesn't use it.
Matt Casto
A: 

I don't see much wrong with the code. Technically you're binding an instance of the Person class (ie it doesn't make sense to try and bind to an interface anyway) I don't know what your PolicyInjection.Wrap method does, but I'm assuming it returns a concrete Person class? Anyway, I just tried this on my end and it works fine...

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        IPerson person = new Person() { FirstName = "Hovito" };

        this.DataContext = person;
    }
}

public class Person : IPerson
{
    public virtual string FirstName { get; set; }
    public string LastName { get; set; }
}

public interface IPerson
{
    string FirstName { get; set; }
    string LastName { get; set; }
}

I would suggest you look into that PolicyInjection class a bit more. Find out if it really does return a Person type as you expect.

Hovito
+1  A: 

We bind to almost nothing but Interfaces in our project, all without problem. The problem you're experiencing is due to entlib... but I'm not familiar enough with entlib to help you there. WPF can, however, bind to Interfaces.

cranley