none
Binding to Explicit Interface

    Question

  • Hi!

    Can you help me understand why this does not work?

    In summary, i need to bind the items in a combobox to a property exposed by an object through an explicit interface.

    the xaml is:

     <Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    xmlns:local="clr-namespace:WpfApplication1">
    <Grid>
    <ComboBox Height="20" Margin="22,15,12,0" Name="comboBox1" VerticalAlignment="Top"
    IsSynchronizedWithCurrentItem="True"
    ItemsSource="{Binding}" DisplayMemberPath="(local:IAge.age)" />
    </Grid>
    </Window>


    the DataContext of the Windows is assigned to an ObservableCollection<Person>, the Person class implements an interface called IAge that exposes the property "age".

    The code behind is:
    public partial class Window1 : Window
        {
            System.Collections.ObjectModel.ObservableCollection<Person> mcoPersons = new System.Collections.ObjectModel.ObservableCollection<Person>();
            public Window1()
            {
                InitializeComponent();
                Person p=new Person();  (p as IAge).age=5; p.name = "A";
                mcoPersons.Add(p);

                p = new Person(); p.name = "B"; (p as IAge).age = 10;
                mcoPersons.Add(p);

                this.DataContext = mcoPersons;
            }

            public interface IAge
            {
                int age { get; set; }
            }

            public class Person : IAge
            {
                public string name { get; set; }
                #region IAge Members
                int _age = 0;
                int IAge.age
                {
                    get
                    {
                        return _age;
                    }
                    set
                    {
                        _age=value;
                    }
                }
                #endregion
            }
        }

    In this examples the combo appears empty. In other cases i get an exception "Key cannot be null.\r\nParameter name: key" thrown at:

          at System.Collections.Specialized.ListDictionary.get_Item(Object key)
           at System.Collections.Specialized.HybridDictionary.get_Item(Object key)
           at System.ComponentModel.PropertyChangedEventManager.PrivateAddListener(INotifyPropertyChanged source, IWeakEventListener listener, String propertyName)...

    is that syntax "(local:IAge.age)" supported at all by WPF in the Path of a Binding? The problem only occurs in DisplayMemberPath, if I use it in other controls as the content (Textblocks, etc.) it works ok.

    thanks
    dan
    • Edited by d.firka Friday, August 28, 2009 1:45 AM
    Friday, August 28, 2009 1:41 AM

Answers

  • I see what you're saying. I wasn't sure if you wanted an explicit interface implementation.

    In your XAML, try the following instead of your current ComboBox:

    <ComboBox  Margin="20" Name="comboBox1" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Path=(local:IAge.age)}" />
            </DataTemplate>                
        </ComboBox.ItemTemplate>
    </ComboBox>
     
    Hope this helps.
    • Edited by dmikon Friday, August 28, 2009 6:05 PM Added 'IsSynchronizedWithCurrentItem'
    • Marked as answer by d.firka Friday, August 28, 2009 7:11 PM
    Friday, August 28, 2009 6:03 PM

All replies

  • Try this:

    XAML:

    <ComboBox Height="20" Margin="22,15,12,0" Name="comboBox1" VerticalAlignment="Top" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" DisplayMemberPath="{Binding age}" />

    Code:

    public class Person : IAge
    {
        public string name { get; set; }
        #region IAge Members
        int _age = 0;
        public int age
        {
            get
            {
                return _age;
            }
            set
            {
                _age=value;
            }
        }
        #endregion
    }
    Friday, August 28, 2009 2:46 AM
  • Thanks dmikon,

    That works, but I will really prefer to use the interface, I have objects with lots of funcionality that is factored using interfaces.

    Suppose a chart object,  IData interface manages the data contained within the chart, IStyle is for the colors and fonts, and IPref for certain preferences like ticks displayed, etc.

    Is seems more "clean" to expose the properties through each interface:

     IStyle s=(myChart as IStyle);

    and then set the properties related to style, and so forth. The method you correctly point out would create a lot of properties in my case, decreasing readability.

    Is there another design pattern for this issue? I know that this is not the approach followed by WPF objects, but I don't know what is wrong with my approach .

    So back to my original post, can I do the binding to the explicit interface?

    dan
    Friday, August 28, 2009 12:49 PM
  • There is something problematic about this type of binding to interfaces in WPF. Maybe they are not supported, but they work in certain cases!

    I have a simple scenario to reproduce the problem:

    1) Create an empty WPF Application project.
    2) Replace the following code behind:

    using System.Windows;
    
    namespace WpfApplication1
    {
        public interface IAge
        {
            int age { get; set; }
        }
    
        public class Person : IAge
        {
            int _age = 0;
            int IAge.age
            {
                get { return _age; }
                set { _age = value; }
            }
        }
       
        public partial class Window1 : Window
        {
            System.Collections.ObjectModel.ObservableCollection<Person> mcoPersons = new System.Collections.ObjectModel.ObservableCollection<Person>();
    
            public Window1()
            {
                InitializeComponent();
    
                // Add Two persons
                Person p=new Person();  (p as IAge).age=5; 
                mcoPersons.Add(p);
                p = new Person(); (p as IAge).age = 10;
                mcoPersons.Add(p);
    
                // Set DataContext
                this.DataContext = mcoPersons;
            }
    
        }
    }
    3) Replace the Window1.xaml with:


    <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
        xmlns:local="clr-namespace:WpfApplication1">
        <StackPanel>
            
            <ComboBox  Margin="20" Name="comboBox1"
                      IsSynchronizedWithCurrentItem="True"
                      ItemsSource="{Binding}"  DisplayMemberPath="(local:IAge.age)" />
            
            <TextBox Height="19" HorizontalAlignment="Left" Margin="20"  
                Text="{Binding ElementName=comboBox1, Path=SelectedItem.(local:IAge.age)}" />
        
        </StackPanel> 
    </Window>

    Run this project, you will see that the combo shows both values of Age correctly, and the TextBox is updated using binding.

    Now, just comment out the TextBox, and the combo binding stops working !!!!

    Any comments? Any suggestions?

    dan

    Friday, August 28, 2009 5:12 PM
  • I see what you're saying. I wasn't sure if you wanted an explicit interface implementation.

    In your XAML, try the following instead of your current ComboBox:

    <ComboBox  Margin="20" Name="comboBox1" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Path=(local:IAge.age)}" />
            </DataTemplate>                
        </ComboBox.ItemTemplate>
    </ComboBox>
     
    Hope this helps.
    • Edited by dmikon Friday, August 28, 2009 6:05 PM Added 'IsSynchronizedWithCurrentItem'
    • Marked as answer by d.firka Friday, August 28, 2009 7:11 PM
    Friday, August 28, 2009 6:03 PM
  • Thanks dmikon!

    do you know why the DisplayMemberPath does not produce the same result consistently?

    dan
    Friday, August 28, 2009 6:24 PM