C# WPF Tutorial - Priority Bindings [Beginner]


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:

Three states of the binding

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