Follow Me on Instagram Subscribe via RSS Feed

Binding to an Indexed Property In Silverlight

July 23, 2010

Since Silverlight 2, the binding in Silverlight has been truly amazing to build applications with.  It’s ease and power has allowed developers to build advanced relationships with very little code.

Silverlight 4 has extended the binding capabilities by adding indexed property binding.  In previous versions, you could bind to an object’s properties like such:

<TextBox Text={Binding LastName}/>

With the addition of indexed property binding, you can bind to indexed objects as follows:

<TextBox Text={Binding [LastName]}/>

The added features open up some new potentials in binding to dynamic values.

The most obvious usage is to bind to a Dictionary object.  However, there is one fallback to binding to Dictionaries.  The Dictionary object does not implement the INotifyPropertyChanged interface.  So changes to the Dictionary does not reflect in your binding. 

There are a couple of approaches to solving this problem.  The first is to make the Dictionary a property of a class that implements the INotifyPropertyChanged interface.  When there is a change made to the Dictionary object, you can call the PropertyChanged event for the Dictionary property and force the binding update.

Here is an example of a class that does this:

    public class Person : INotifyPropertyChanged
    {
        private Dictionary<string, string> values;

        public Dictionary<string, string> Values
        {
            get { return values; }
            set
            {
                values = value;
                if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Values"));
            }
        }

        public Person()
        {
            Values = new Dictionary<string, string>();
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

    }

Now there is an obvious flaw with this design.  It only fires the PropertyChanged event when the entire Dictionary is updated.  This solution might work when your implementation is doing a flush/fill of the data in the Dictionary.  However, if you need to dynamically modify the contents of the Dictionary, this solution breaks down.

The solution to this is to create a custom Dictionary object that implements the INotifyPropertyChanged interface.  This solution is rather straight forward and simply wraps a standard Dictionary.  This solution is meant to be simple and does not support all of the features the Dictionary object has.  However, the class could be extended to encompass more functionality if required.

So let’s take a look at the solution:

    public class NotifyDictionary<TKey, TValue>: INotifyPropertyChanged
    {
        private Dictionary<TKey, TValue> valueDict = new Dictionary<TKey, TValue>();

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        public TValue this[TKey key]
        {
            get
            {
                if (valueDict.ContainsKey(key))
                {
                    return valueDict[key];
                }
                else
                {
                    return default(TValue);
                }
            }
            set
            {
                if (valueDict.ContainsKey(key))
                {
                    valueDict[key] = value;
                }
                else
                {
                    valueDict.Add(key, value);
                }

                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(String.Empty));
            }
        }

        public void LoadDict(Dictionary<TKey, TValue> newVals)
        {
            valueDict = newVals;
            if(PropertyChanged != null) 
                PropertyChanged(this, new PropertyChangedEventArgs(String.Empty));
        }
    }

There are a couple of things worth mentioning here.

First, the only data entry points are thru a direct index referencing or the LoadDict() method which simply replaces the current dictionary.

Second, you cannot fire a PropertyChanged event on a single index within the dictionary.  Therefore, every time you update an index, the entire object will fire the PropertyChanged event.  This will have the effect of updating all bindings to your dictionary, regardless of the index.

Well there you have it.  You can now bind to indexed properties that will update when the underlying Dictionary is updated.

This code is the basis for a custom localization solution that I will blog about later.

Filed in: Silverlight • Tags: ,

Leave a Reply