Updates
- I made the Silverlight version identical to the WPF version, so you can use
PriorityObject
the same way on both platforms.
- 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
:
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.
- A
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).
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:
- A the beginning, it has a default value: "Fast text #1".
- After 3 seconds, it gets a new value: "Slower text #1".
- 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
PriorityObject
s complete their
task at the right moment, after 5 seconds exactly (as we expect).
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
<ListBoxx:Name="AdvancedListBox"Width="280">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinitionWidth="115"/>
<ColumnDefinitionWidth="115"/>
<ColumnDefinitionWidth="40"/>
</Grid.ColumnDefinitions>
-->
<TextBlockGrid.Column="0"Text="{Binding Text1.Value}"/>
-->
<TextBlockGrid.Column="1"Text="{Binding Text2}"/>
-->
<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
public class AdvancedAsyncListItemWithPriorityObject : INotifyPropertyChanged
The item class has to implement the interface INotifyPropertyChanged
.
Text1
Property
public PriorityObject<string> Text1 { get; set; }
We need the get and set, otherwise ListBox will not update the text.
Initialization in the class builder
var valueDelegatesForText1 = new PriorityObjectDelegates<string>();
valueDelegatesForText1.Add(
() =>
{ Thread.Sleep(5000); return "SloweST text #1"; });
valueDelegatesForText1.Add(
() =>
{ Thread.Sleep(3000); return "Slower text #1"; });
Text1 = new PriorityObject<string>(
"Text1", this.NotifyPropertyChanged, valueDelegatesForText1, true,
"Default quickest text");
I deployed this example, in order to show its structure. See Double1 for a compact example.
Double 1
Property
private PriorityObject<double> _Double1;
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
_Double1 = new PriorityObject<double>
("Double1", this.NotifyPropertyChanged, new PriorityObjectDelegates<double>()
{ ()=>
{ 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
public event PropertyChangedEventHandler PropertyChanged;
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 PriorityObject
s.
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:
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:
public AdvancedAsyncListItemWithPriorityObject(
System.Windows.Threading.Dispatcher
CreatorSDispatcher)
Then the item builder give the same dispatcher to the PriorityObject's builder:
_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
PriorityObject
s? 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.