1

I have recently discovered indexed properties. This looks like the perfect solution to the scenario in which the data I am working with would best be expressed in a collection, yet still needs to be implemented as a property that can be used in XAML databinding. I started with just a test of creating indexed properties, and I had no problems there, but I just don't seem to be able to get the binding to work.

Can anyone point out where I'm going wrong?

Here is the test class with a nested class to create the indexed property:

public class TestListProperty 
{

    public readonly ListProperty ListData;

    //---------------------------
    public class ListProperty : INotifyPropertyChanged 
    {
        private List<string> m_data;

        internal ListProperty()
        {
            m_data = new List<string>();
        }

        public string this[int index]
        {
            get
            {
                if ( m_data.Count > index )
                {
                    return m_data[index]; 
                }
                else
                {
                    return "Element not set for " + index.ToString();
                }
            }
            set
            {
                if ( m_data.Count > index )
                {
                    m_data[index] = value;
                }
                else
                {
                    m_data.Insert( index, value );
                }
                OnPropertyChanged( "Item[]" );
                Console.WriteLine( value );
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged( string propertyName )
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if ( handler != null ) handler( this, new PropertyChangedEventArgs( propertyName ) );
        }

    }
    //---------------------------
    public TestListProperty()
    {
        ListData = new ListProperty();
    }

}

Here is the XAML:

<Window x:Class="TestIndexedProperties.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <TextBox Width="200" Grid.Row="0" Text="{Binding Path=ListData[0], Mode=TwoWay}" />
        <TextBox Width="200" Grid.Row="1" Text="{Binding Path=ListData[1], Mode=TwoWay}" />
        <TextBox Width="200" Grid.Row="2" Text="{Binding Path=ListData[2], Mode=TwoWay}" />


    </Grid>
</Window>

And here is the Window code:

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

        TestListProperty test = new TestListProperty();

        this.DataContext = test;

        test.ListData[0] = "ABC";
        test.ListData[1] = "Pleeez 2 wurk?";
        test.ListData[2] = "Oh well";

    }
}

Thanks for any and all help!

Arian Goodwin
  • 13
  • 1
  • 6

1 Answers1

0

There's no mechanism in your code to indicate to the front-end when the list has changed, i.e. ListProperty is implementing INotifyPropertyChanged instead of INotifyCollectionChanged. The easiest fix would be to change m_data to type ObservableCollection and bind your XAML controls to that instead, although that probably defeats the purpose of what you're trying to do in the first place. The "proper" way is to subscribe to the CollectionChanged event and propagate the messages through:

public class TestListProperty
{
    public ListProperty ListData { get; private set; }

    //---------------------------
    public class ListProperty : INotifyCollectionChanged
    {
        private ObservableCollection<string> m_data;

        internal ListProperty()
        {
            m_data = new ObservableCollection<string>();
            m_data.CollectionChanged += (s, e) =>
            {
                if (CollectionChanged != null)
                    CollectionChanged(s, e);
            };
        }

        public string this[int index]
        {
            get
            {
                if (m_data.Count > index)
                {
                    return m_data[index];
                }
                else
                {
                    return "Element not set for " + index.ToString();
                }
            }
            set
            {
                if (m_data.Count > index)
                {
                    m_data[index] = value;
                }
                else
                {
                    m_data.Insert(index, value);
                }
                Console.WriteLine(value);
            }
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged;

    }
    //---------------------------
    public TestListProperty()
    {
        ListData = new ListProperty();
    }
}
Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • I have the code up and running now. Many units of gratitude to you for the assistance! – Arian Goodwin Dec 12 '13 at 16:54
  • How are you propagating the messages? I can't see any notification called – Simon Bosley Jan 05 '16 at 10:26
  • Change notification occurs by raising the CollectionChanged event. The m_data collection generates its own CollectionChanged events, so the parent wrapper needs to subscribe to those and raise the event for itself. INotifyPropertyChanged isn't required in this case because m_data has a private setter and thus can never be changed. – Mark Feldman Jan 05 '16 at 23:37