Bindings, bindings, bindings. WPF is all about bindings - and you can't
be all about bindings unless there are a whole bunch of different types
to play with. We have looked at a couple different types here - I won't list them all, it is probably just easier to look at
list in the WPF category. Today we are going to
introduce yet another type of binding - the Priority Binding.
What is a priority binding, you ask? Well, it is a binding with a number
of possible sources, with the sources ordered by priority. The source
with the highest priority that returns a value successfully becomes the
active binding in a priority binding. The key here, though, is that the
active binding can change. If a low priority source returns first, it
will become the active binding. But as soon as a source with higher
priority returns successfully, it will supersede the lower priority
source and become the active binding.
You are probably wondering about how this is actually useful. Well,
there is a great example for this - take Microsoft Word. When you open a
document in Word, there is a page count displayed in the bottom status
bar. If you've ever opened a really long document in Word, you may have
noticed that that page count is not actually correct until the document
finishes loading - for a couple seconds, it shows an approximate page
count that keeps changing. This is a perfect use of a priority binding.
The slowest, but highest priority binding would return the correct
number of pages - but there would also be a fast, low priority binding
that would return the approximate number of pages. The software would
use the fast low priority method until the slow, high priority method
finally returned with a value. Pretty much, priority bindings can be
useful whenever there is something slow and asynchronous that is
affecting your UI.
Enough with this talk - lets look at some code!
<Window x:Class="PriorityBindingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Priority Binding Test" Height="100" Width="200"
Name="MainWindow">
<Grid>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Text>
<PriorityBinding>
<Binding ElementName="MainWindow" Path="Slow" IsAsync="True" />
<Binding ElementName="MainWindow" Path="Medium" IsAsync="True" />
<Binding ElementName="MainWindow" Path="Fast" />
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
OK, here we have a simple window defined in XAML, with a single
TextBlock
. It is the content of the TextBlock
that we are interested
in, because that is where the PriorityBinding
is. To use a priority
binding, you make a PriorityBinding
where you would usually make a
regular binding, and then fill the PriorityBinding
with other
bindings. The order of the other bindings here is very important - the
order determines priority. The first binding in the list has the highest
priority, and the second has the second highest, and so on and so forth.
Here we have three bindings, each to a different path: Slow
, Medium
,
and Fast
. The slow one will obviously be the slowest (because we are
going to code it that way), but seeing that it comes first, it has the
highest priority.
There is one important flag on some of these bindings that you may not
recognize:
IsAsync="True"
. This is actually very important - without
it, priority bindings wouldn't be very useful. Essentially, it means "go
try and get that value asynchronously". We don't want the main thread to
try and determine the value for the slow or medium binding - because if
it did, the app would just sit there as the main thread tried to resolve
the binding. It would make the priority pointless, because the
application would sit and process the highest priority binding, ignoring
everything else until the binding returned a value.
But with that
IsAsync
flag, the main application thread tells a thread
pool thread to go off and process that binding - leaving the main thread
free to continue on. So in a priority binding, everything but the lowest
priority (and hopefully fastest) binding should be set to async. The
reason that we don't set the last binding to async is that we generally
want to return at least some value for the binding immediately. You can
set the last binding to async if it also takes some time - this just
means that there will be a period of time where this binding has no
value.
OK, now for the C# code for the properties these three bindings are
hitting against:
public string Slow
{
get
{
Console.WriteLine("Slow: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
return "This is the slowest value. I'm so slow.";
}
}
public string Medium
{
get
{
Console.WriteLine("Medium: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
return "Medium! Medium! Woohoo!";
}
}
public string Fast
{
get
{
Console.WriteLine("Fast: " + Thread.CurrentThread.ManagedThreadId);
return "Wow, this is fast!";
}
}
Essentially, all we are doing here is sleeping the slow and medium for a
few seconds before returning the value. And just to prove that the
requests are actually coming in off of separate threads, I threw in some
Console.Writeline
calls printing out the the thread ids:Slow: 7
Fast: 10
Medium: 11
And in fact, if you look at more of the details about the threads
hitting the property values, you will find that the thread hitting
Fast
is in fact the main application thread, and the threads hitting
Slow
and Medium
are background worker thread pool threads.
So, in the end, the application transitions through three states that
look like this:
The application initially looks like the first window. After a second,
the text changes to what is displayed in the second window. And after
another second, the
Slow
binding finally kicks in, and the text
changes to what is in the third window
And there you go - that is it for an introduction to priority bindings!
You can download the source for this simple Visual Studio project
here, if
you would like to play around with it.
Source Files:
Comments
Post a Comment