Click here to Skip to main content
15,881,455 members
Articles / Desktop Programming / WPF

PriorityObject: More fluent than PriorityBinding in list context (as ListBox), and compatible with Silverlight and WP7

Rate me:
Please Sign up or sign in to vote.
4.00/5 (3 votes)
15 May 2012Ms-PL6 min read 22K   161   3   1
PriorityObject : more fluent than PriorityBinding in list context (as ListBox), and compatible with Silverlight and WP7.

Updates

  1. I made the Silverlight version identical to the WPF version, so you can use PriorityObject the same way on both platforms.
  2. Now there is a Windows Phone 7 example in the archive also.

Reason 

Recently, I tried to use PriorityBinding in a WPF ListBox, but it was extremely slow and erratic. So I tried to code a more fluent replacement. At last, I created PriorityObject, and now I can display a fluent ListBox updating smoothly and quickly.

Recall: PriorityBinding is a binding with multiple sources, some quick, others slow. Usually, the slowest binding has the best result, and the fastest has the worst result. For example, imagine the source of a an image: the fastest binding will be a blur thumbnail, and the slowest will be the complete high-resolution picture. Of course, there is a priority in the sources, so when the best source is ready, the other sources are ignored then. In our example, if the complete picture is available before the thumbnail, the thumbnail will be ignored and only the picture will be displayed.

A comparison with PriorityBinding

There are three main differences between .NET's PriorityBinding and my PriorityObject:

  1. PriorityObject uses Threads and therefore is much more 'fluent', especially in list controls (as ListBox). I presume PriorityBinding uses BackgroundWorker, which is fine with isolated bindings but chaotic with lists.
  2. PriorityBinding is partly described in the XAML itself, which is a violation of the rule about separation of the visual description and the object implementation. On the contrary, PriorityObject let the XAML be ordinary while the object implementation manages the content (with threads).
  3. PriorityObject is compatible with Silverlight.

Other advantages:

Your value can be lazy (evaluated on demand). Just choose Lazy=true when you call the builder of PriorityObject. That is very useful when the ListBox is not always displayed (it can be in a tab or in a secondary window, for example).

Inconvenience: Your procedures are evaluated in threads. So you will have to manage the thread headaches with WPF's objects. I can not do anything against that, this is a big limitation of WPF, but you can use Dispatchers to do some of the work on the UI thread (or your object's Dispatcher). Anyway, this problem have to do with threads and WPF, so you cannot avoid it in parallel programming whether you use PriorityObject or not.

Example of two ListBox launched in parallel

Both ListBox display two texts, one with priority evaluation functions (on the left, the text #1) and one ordinary (the text #2). The left text (#1) has three states:

  1. A the beginning, it has a default value: "Fast text #1".
  2. After 3 seconds, it gets a new value: "Slower text #1".
  3. After another 2 seconds, it gets its final value: "SloweST text #1".

The ListBox on the left uses .NET's PriorityBinding, and the ListBox on the right uses PriorityObject and have a number as an additional column (its value change after 2 seconds).

I populate both ListBoxes at the same time. As you can see on this screen shot, after 6 seconds, the PriorityBindings are not completed, they even mix the three states on different lines. They need 10 seconds to complete the list update. On the contrary, the PriorityObjects complete their task at the right moment, after 5 seconds exactly (as we expect).

Image 1

Code example

Let's implement the previous example, with ListBox displaying items made of two texts and a number. The first text and the number are based on PriorityObject, the second text is an ordinary binding so I do not describe it.

XAML

XML
<ListBoxx:Name="AdvancedListBox"Width="280">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinitionWidth="115"/>
          <ColumnDefinitionWidth="115"/>
          <ColumnDefinitionWidth="40"/>
        </Grid.ColumnDefinitions>

        <!-- We have to add '.Value' for PriorityObject: -->
        <TextBlockGrid.Column="0"Text="{Binding Text1.Value}"/>

        <!-- An ordinary static text: -->
        <TextBlockGrid.Column="1"Text="{Binding Text2}"/>

        <!-- We don't need to add '.Value' here because Double1 encapsulates _Double1.Value: -->
        <TextBlockGrid.Column="2"Text="{Binding Double1}"/>

      </Grid>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

As you can see, we have three bindings: 

  • Text1.Value, directly bound to the content of a PriorityObject<string>.
  • Text2, bound to an ordinary string with get/set.
  • Double1, indirectly bound to a PriorityBinding<string>, via Double1 : get { return _Double1.Value; }. That needs two definitions in the CS but has the advantage of the clarity in the XAML.

CS

The item class

C#
public class AdvancedAsyncListItemWithPriorityObject : INotifyPropertyChanged 

The item class has to implement the interface INotifyPropertyChanged.

Text1 

Property

C#
public PriorityObject<string> Text1 { get; set; } 

We need the get and set, otherwise ListBox will not update the text.

Initialization in the class builder 

C#
var valueDelegatesForText1 = new PriorityObjectDelegates<string>();

// First evaluation function: slowest & best
valueDelegatesForText1.Add(
    () =>
   
{    Thread.Sleep(5000); // This simulates a lengthy time before the data being bound to is actualy available.
        return "SloweST text #1";    });

// Second evaluation function: quicker (but sill slow) and worse
valueDelegatesForText1.Add(
    () =>
   
{    Thread.Sleep(3000); // This simulates a lengthy time before the data being bound to is actualy available.
        return "Slower text #1";    });

Text1 = new PriorityObject<string>(
    "Text1", this.NotifyPropertyChanged, valueDelegatesForText1, true, 
    "Default quickest text"); // a default quick but worst text.

I deployed this example, in order to show its structure. See Double1 for a compact example.

Double 1

Property

C#
private PriorityObject<double> _Double1;

// Double1 encapsulates _Double1 in order to avoid the addition of ".Value" in the XAML:
public double Double1
{ get { return _Double1.Value; } set { _Double1.Value = value; } }

In order to simplify the binding in the XAML, we use Double1 to access to _Double1.Value.

Initialization in the class builder

C#
_Double1 = new PriorityObject<double>
    ("Double1", this.NotifyPropertyChanged, new PriorityObjectDelegates<double>()
{    ()=>
   
{        // This simulates a lengthy time before the data being bound to is actualy available.
        Thread.Sleep(2000);
        return 2000.0;    }
}, false); 

This example is a bit more compact than the one with Text1. And there is only one thread/evaluation/source function, as even with only one source PriorityObject is easier than the ordinary thread programming. Please note we give "Double1" as the notification name, and not "_Double1", because the ListBox is bound to Double1.

The INotifyPropertyChanged

C#
public event PropertyChangedEventHandler PropertyChanged;

// This standard procedure is necessary to PriorityObject.
private void NotifyPropertyChanged(string info)
{    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(info));
}

A standard implementation of INotifyPropertyChanged. Please note we absolutely need the NotifyPropertyChanged procedure for the PriorityObjects.

Differences in Silverlight / Windows Phone 7 

Currently, there is no difference.

I initially wrote an adaptation for Silverlight because it needed the right Dispatcher to invoke the NotifyPropertyChanged procedure.

It appears Silverlight uses only one dispatcher, so I modified my code, then the following paragraph is now useless, until a future version of Silverlight uses several dispatchers. In that case, you can adapt the code of PriorityObject.cs by allowing its define "RequestObjectSDispatcher".

Obsolete adaptation 

[ This paragraph is obsolete, except if some day Silverlight uses several Dispatchers (so I do not delete it) ]  

Due to limitations of Silverlight, there are some small differences.

Mainly, we need to know the Dispatcher of the class/thread/function that instances a PriorityObject-based item class.
Here is an adaptation of the code:

C#
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
   
        var list = new
ObservableCollection<AdvancedAsyncListItemWithPriorityObject>();
            for (int i = 0; i < 10; i++)
   
            list.Add(new
AdvancedAsyncListItemWithPriorityObject(
#if SILVERLIGHT
                    this.Dispatcher
#endif
                    ));
            this.AdvancedListBox.ItemsSource = list;
        } 

As you can see, we need to give the dispatcher to the item class builder. That is easy, usually there is a this.Dispatcher. If not, try null (PriorityObject will use a default dispatcher).

An example of class builder:

C#
public AdvancedAsyncListItemWithPriorityObject(
    System.Windows.Threading.Dispatcher
CreatorSDispatcher) // With Silverlight, we need to know the Dispatcher that created this instance.

Then the item builder give the same dispatcher to the PriorityObject's builder:

C#
_Double1 = new PriorityObject<double>
    ("Double1", this.NotifyPropertyChanged, new PriorityObjectDelegates<double>()
{    ()=>
    {
        Thread.Sleep(2000); return 2000.0;
    }
}, CreatorSDispatcher, false);  

Here, the only difference with the Dot.net code is the addition of this parameter: CreatorSDispatcher (in plain English: Creator'sr dispatcher).

Download the source code and some examples

The archive contains the source code of PriorityObject and two example projects: one for WPF/>NET, and one for Silverlight.

FAQ

  • Do we really need the NotifyPropertyChanged procedure for the PriorityObjects? Why don't we access directly to PropertyChanged in PriorityObject?
    • A: PropertyChanged is an event, and an event is compiled an unusual way in the C#, so we cannot invoke it outside its class. That is why we need to give the NotifyPropertyChanged reference to the builder of PriorityObject (as NotifyPropertyChanged is not officially part of INotifyPropertyChanged, how strange it may appear to us).
  • In Silverlight, could we extract the Dispatcher from the instance of the PriorityObject we are building ?
    • A: Yes, I updated my code to internally use the only one Dispatcher instantiated by a SL app.

Open source

The license of this code is the Ms-PL, which basically allows you to use the source code in your programs, open or closed. If you modify the code, you don't have to publish your modifications. Something you can appreciate in this license: it warrants its author (I here) will not claim anything about patents (this plague of modern programming). Many licenses do not say anything about patents and expose you (yes, including the most famous licenses). See here for some details.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior) independent
France France
Hi !

I made my first program on a Sinclair ZX-81.
Since that day, I have the virus of computers. Smile | :)

Here is my website:
https://chrisbertrand.net

And my blog:
https://chrisbertrandprogramer.wordpress.com/

Comments and Discussions

 
GeneralMy vote of 2 Pin
Member 1098436431-Jul-14 13:20
Member 1098436431-Jul-14 13:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.