Bill Tudor

Weblog

  Home  |   Contact  |   Syndication    |   Login
  49 Posts | 0 Stories | 95 Comments | 0 Trackbacks

News

Copyright © Bill Tudor

Archives

Post Categories

The Scenario goes something like this:

  • A data visualization application
  • There are literally tens of thousands of “data items”
  • The data is stored remotely on a server
  • The data items are identified by unique “tags”, which are strings

I want to allow WPF designers to bind to this data by using XAML binding syntax knowing only the tags. The required data item values would be fetched as needed from the server. Only the items currently used in the UI would be fetched.

After many days thinking about this problem and after several failed implementation attempts (most using reflection in one way or another), I stumbled upon what may be an excellent solution this morning. The solution is described in general terms below.

Data Binding to Indexer Properties

The key (no pun intended) is data binding to indexer properties, using a string (the “Tag”) as the index key. The WPF documentation describes this feature, but I must have failed to notice. The indexer is typically an integer, but it can be any object – the “tag” of a data item in this case. Designers can use the {Binding Path=[tag]} XAML syntax (note the use of square brackets), utilizing whatever tags they need.

<StackPanel Orientation="Horizontal">
    <TextBlock Text="Value 1:"/>
    <TextBlock Text="{Binding Path=[variable1]}" />
</StackPanel>

In the example above, the key “variable1” is specified in the binding path to the text block.

The data context is then set to an object that contains an indexed property using a String type as the indexer key.

public Window1()
{
    InitializeComponent();
    this.DataContext = new BindingObject();
}

When retrieving data, the Get method for the indexed property will first check its local data cache, returning the data already available. Otherwise, the Get method will access the remote data store to bring it into the cache.

public class BindingObject : INotifyPropertyChanged
{
    private PropertyChangedEventArgs _dataChangedEventArgs = new PropertyChangedEventArgs("");
    private Dictionary<String, double> _data;
    public double this[string index]
    {
        get
        {
            if (!_data.ContainsKey(index))
            {
                double valueFromServer = 0.0;
                // ... Fetch data from server
                _data.Add(index, valueFromServer);
            }
            return _data[index];
        }
    }
    // Rest of object ...

Using this technique, there is no need to pull in all of the thousands of data points potentially available – only the ones that are used in binding expressions are retrieved. The indexer syntax also allows a binding path without having to create properties on the data context object for each tag. I can also think of several optimizations that would allow less trips over to the remote data store (maybe these will be discussed in a future post).

What if the data changes?

Occasionally, the values of the data items are updated. How will this implementation support dynamic data updates? The answer could be the INotifyPropertyChanged interface. (As far as I know, an indexed property cannot be a dependency property). I though about having a dependency property which itself contained an indexer property, but I could not figure out how to formulate the XAML binding syntax for that. The question was, what “Property” has changed for the notify event?

To my surprise, raising the PropertyChanged event using String.Empty as the property name works! The WPF binding system must assume this is the indexed property, then dutifully performs updates on ALL of the bound tags (indexer keys) from this single event. This behavior is perfect for my scenario. The remote server will notify me of data update, the local application will update its local cache for all tags currently in use, then raise a single PropertyChanged event on the property named “” and all of the bindings in the UI are updated. Wow.

Two-Way Binding

Two-way binding is not going to be a problem either; just add a setter to the indexed property. When the setter is invoked, a call is made to the remote server to update the data for the indicated tag.

Better ways to do this?

Are there better ways to do this? Probably. I plan to keep looking. My scenario requires good performance and a decent amount of scalability. The remote data can sometimes update quickly (several times per second), and local UIs may contain hundreds of elements bound to specific tags. I need a solution that is bound by either the network or graphics re-painting – the binding infrastructure should be negligible. I plan to perform some scalability tests using this technique to see if the requirements for my scenario are met. If anyone knows some answers, I'm all ears.

posted on Sunday, May 31, 2009 10:17 PM

Feedback

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 7/19/2009 9:52 PM Ravi
Hi Bill,

This article was extremely useful. I have been struggling with indexed properties and data binding. The MVVM sample project from MSFT ignored the case where the property name can be empty or is "Item[]" by throwing an exception in the ViewModelBase class (which misled me).

Thanks again for your article.

Regards,
Ravi


# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 7/20/2009 5:06 AM Bill
You are welcome.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 8/4/2009 4:21 AM Smarty
Thanks for this, but it seems that it works only with default properties. Can you verify that something like this does not work? Or maybe you can give me hint how to get it work?

"{Binding Path=propName1[variable1]}"

Thank you,
Smarty

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 8/4/2009 6:38 PM Bill
I believe what you are asking is possible; binding to array elements does work. The following label witll have "one" in it.

code:
public partial class Window1 : Window
{
private string[] _atest = new string[] { "one", "two" };
public Window1()
{
InitializeComponent();
this.DataContext = this;
}
public string[] atest
{
get
{
return _atest;
}
}
}

xaml:
<Label Content="{Binding atest[0]}" />


# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 11/17/2009 8:24 AM Tim
Thanks for the example. I am having the most difficult time finding ANYONE to show bindings from UI elements to below dataobjects (Lists, Dictionaries, Arrays, whatever) PROGRAMATICALLY!! Scenario: I need to add TextBoxes or Labels programatically to my UI layout and cannot, for the life of me, find one example of binding the TextBox's text property to a keyed collection underneath PROGRAMATICALLY so that at the Source, (i.e., business layer) I am passed an index (even the Name, Tag or other identifying data I can know what the key is.
Help!

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 11/22/2009 4:16 PM Mojo
Tim, if you are still struggling with that, let me know at weeks.josh at gmail. I think I could probably help you out.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 12/14/2009 4:20 PM Alex
This is just what I was looking for. Thanks. I couldn't figure out how to send the change notifications for the indexer property. BTW, sending String.Empty for the property name in the change notification works for ANY property. It is basically a wild card that says something changed but you don't know what.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 12/23/2009 6:42 PM Lee
Hello,
I'm a Chinese.If I can't describe my problems clearly with my poor English,please don't mind.Thanks!
This article is useful.But I still have some problems.Would you mind if I describe my problems by VB.net?Because I find that there aren't ordinary indexes properties in C# like this:

Public Class MyData
Private _mynum(5) As Int32
Public Property MyNum(i As Int32) As Int32
Get
Return _mynum(i)
End Get
Set(value As Int32)
_mynum(i) = value
'Here is some code that must work with index like this, which limits the Sum of Array:
If _mynum.Sum > 100 Then
_mynum(i) -= _mynum.Sum - 100
End If
'So I can't use the property which returns an Array:
'Public Property MyNum() As Int32()
End Set
End Property
End Class

It seems that it isn't correct grammar like this in C#:
public int MyNum[int i]{get;set;}
What can I do if I want to bind MyNum(0) to MyTextBox.Text?Thanks for your concerning.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 12/24/2009 8:35 AM Bill
I see your comment that you cannot use the property that returns the array due to the Setter logic, however, this also means that you cannot perform the WPF binding syntax you desire. The 'MyNum' member is doing two jobs: exposing a property and modifying the array elements when setting them.

There are several ways to solve this problem, depending on the particular context. One is presented below for your consideration.

Consider this:

Public Class MyData
Private _mynum() As Int32 = {1, 2, 3, 4, 5}
Public ReadOnly Property MyNum() As Int32()
Get
Return _mynum
End Get
End Property
End Class

A WPF binding expression would be:

<Label Content="{Binding Path=MyNum[0]}" />

Assuming the data context was set to an instance of 'MyData', the label would show '1'. Note that you have to use C-style syntax ('[0]') in the xaml.

You would then handle the 'Set' logic either externally to this class, or with a method call like this:

Public Sub SetMyNum(ByVal index As Int32, ByVal value As Int32)
_mynum(index) = value
'Here is some code that must work with index like this, which limits the Sum of Array:
If _mynum.Sum > 100 Then
_mynum(index) -= _mynum.Sum - 100
End If
End Sub

Hope that helps,

Bill


# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 12/28/2009 9:06 PM Lee
Hello,
Thanks for your reply.After reading it,I realize that it is imposible to construct the binding to an index property as same as the one to an ordinary property.At least it is imposible in VS2008.But I still want to do my job by using whatever like databinding.After a long time,I find that I can make my own "DataBinding".I can "RaiseEvent" when the property changes.Now I'm trying to find a simple method to construct a "TwoWay" binding which is similar to the function BindingOperations.SetBinding.I will always read your articles for your further suggestions.
Wishes your health.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 4/30/2010 12:38 PM Josh Einstein
"To my surprise, raising the PropertyChanged event using String.Empty as the property name works! The WPF binding system must assume this is the indexed property, then dutifully performs updates on ALL of the bound tags (indexer keys) from this single event."

Technically, raising PropertyChanged with an empty string tells observers that the entire object state should be refreshed. It isn't specific to indexed properties. So what I'm trying to figure out how to do is to tell WPF that just a particular indexed property changed since invalidating the entire ViewModel is too excessive.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 4/30/2010 4:15 PM Bill
That's a good point; in my case, the entire model is updated (for the most part), but certaintly that would not be the "usual" case.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 2/14/2011 2:51 AM Raja
Thanks a lot!! this is exactly what I was looking for.

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 3/2/2011 8:26 AM Ben
Sending Binding.IndexerName will refresh indexed properties.

OnPropertyChanged(Binding.IndexerName);

http://10rem.net/blog/2010/03/08/wpf---silverlight-quick-tip-inotifypropertychanged-for-indexer


You can also specify the IndexerName string in an attribute on your class and then send this.


[DefaultMember("Foo")]
class my class ......

OnPropertyChanged("Foo"];



# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 9/10/2011 4:29 AM Giuseppe
Hi Tudor, great post, i found it very usefull, there are no many information for this problem around. Did you do the performance test? have you any update?

# re: WPF Data Binding; Indexed Properties and INotifyPropertyChanged 10/13/2011 6:03 AM Matt Norrie
Cheers Bill,

The part about raising the PropertyChanged using String.Empty was a massive help - I had been trying a variety of strings but never thought of using empty!

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: