Behaviors – Busy State Indicator


Hi again in the second part of discovering Behaviors in WPF/Silverlight with Expression Blend.  In the previous part, we have seen how to build simple behaviors in code using Visual Studio 2010.  In this post, I’m going to extend that and see how to use a “busy state indicator” as a behavior to signal the state of business in your application.

The other day, I was trying to include a busy indicator in my application.  I didn’t want to build this from scratch, and instead wanted to use one of the behaviors available online.  I found one in the Expression Community Gallery that is called Busy State Indicator Behavior by Pedro Pombeiro.  This one is great since it is built as a reusable behavior, customizable, and makes use of a great indicator built by Sasha Barber, a Circular Progress Bar.  Both form a perfect match of what I want.  Details of how to use that in an application follow in this post.

Setting the stage:

In order for us to be on the same page, make sure you have the following in place:

  1. Visual Studio 2010 Beta, you can download it from here.  VS 2008 should be fine as well.
  2. Download Busy State Indicator Behavior from here.

Building an application that uses Busy State Indicator Behavior:

1. Create a project in Visual Studio:

Give the project a name, I have named my project Behaviors101

2. Build the busy state context:

I mean by that the logic needed to indicate when you’re application is busy.  I will build that as a custom class in my project, which will provide two properties (IsBusy and BusyText) designating flag for business and an optional text for elaboration on what your application is busy on.  Below is the initial code for this class that I have named BusyState.cs.

   1: namespace Behaviors101

   2: {

   3:     class BusyState

   4:     {

   5:         private bool isBusy;

   6:         private string text;

   7:  

   8:         public bool IsBusy 

   9:         {

  10:             get { return isBusy; }

  11:             set { isBusy = value; }

  12:         }

  13:  

  14:         public string BusyText 

  15:         {

  16:             get { return text; } 

  17:             set { text = value; }

  18:         }

  19:     }

  20: }

 

3. Use the busy state context in your application:

This step is more of just a reference and instantiation of the BusyState class outlined above.  You can do that either in code or XAML.  I have chosen to do that in XAML and made that possible by referencing the namespace of my application to get access to the BusyState class:

   1: <Window

   2:         ...

   3:         xmlns:local="clr-namespace:Behaviors101"

   4:     

   5:         Title="MainWindow" Height="350" Width="525">

   6:  

   7:     <Window.Resources>

   8:         <!-- Instantiates the BusyState class -->

   9:         <!-- Giving it an x:Key so it's available as a resource -->

  10:         <local:BusyState x:Key="BusyStateObject" IsBusy="False" Text="None"/>

  11:     </Window.Resources>

  12:  

  13:     <Grid>

  14:         ...

  15:     </Grid>

  16: </Window>

 

4. Build the functionality to use the busy state context:

Since I don’t have a functionality for this application, I’m gonna resemble its state of being busy by a timer.  The timer will signal the application from busy to free upon the ticks of the clock.  You may change that with your own logic such as asynchronous internet connections, database queries, or any other long running processes.  Below is the code snippet that declares the timer, initializes it, and wires the event of ticking. 

   1: DispatcherTimer tmr;    

   2:     

   3: public MainWindow()

   4: {

   5:     InitializeComponent();

   6:  

   7:     // create the timer

   8:     tmr = new DispatcherTimer();

   9:     // set the interval to 5 seconds

  10:     tmr.Interval = TimeSpan.FromSeconds(5);

  11:     // wire the event of the timer tick

  12:     tmr.Tick += new EventHandler(tmr_Tick);

  13:     // start the timer

  14:     tmr.Start();

  15: }

  16:  

  17: // timer tick event which simply switched the application from busy to free to busy, ...

  18: void tmr_Tick(object sender, EventArgs e)

  19: {

  20:     // get a reference to the busy state object decalred in XAML as a resource

  21:     BusyState bstate = (BusyState)this.FindResource("BusyStateObject");

  22:  

  23:     // toggle the IsBusy

  24:     bstate.IsBusy = !bstate.IsBusy;

  25:     

  26:     // set the busy text to Busy or Free according to the busy flag

  27:     if (bstate.IsBusy)

  28:     {

  29:         bstate.BusyText = "Busy!";

  30:     }

  31:     else

  32:     {

  33:         bstate.BusyText = "Free...";

  34:     }

  35: }

Notice how I have obtained a reference to the static resource BusyStateObject in line 21, which enables me then to change the busy flag and text.

5. Test out your application busy state by binding to UI

Before we go ahead with the behavior, let’s do a quick test on the above.  I’m gonna bind the BusyStateObject with two elements in the UI in XAML: a textblock and a checkbox.  This will enable us to do a quick check on whether the above is working.

   1: <Grid>

   2:    <TextBlock Height="30" Margin="72,68,218,213" 

   3:         Text="{Binding Source={StaticResource BusyStateObject}, Path=BusyText}" />

   4:    <CheckBox Content="Is Busy?" Height="16" Margin="72,34,0,0" Name="checkBox1" Width="120" 

   5:         IsChecked="{Binding Source={StaticResource BusyStateObject}, Path=IsBusy}" />

   6: </Grid>

The binding as you can see (in line 3 and 5) is simple:  Text property (target property) of the TextBlock (target object) gets its value from the static resource defined above as BusyStateObject (source object) with a path to BusyText (source property).  Same goes for IsChecked of the CheckBox. 

Once you do this in XAML, you’ll see that design time immediately reflects on the BusyStateObject properties by setting IsChecked to false and Text to “None”.  Now, if you run the application (F5) you will expect that the binding will do the job once the timer kicks in.  Unfortunately that is not the case!

Although the timer does its job, you’ll find that changes to your BusyStateObject properties (IsBusy and Text) doesn’t have an effect on the UI and the bound properties, and that is caused by the fact that your application has no clue at all of the changes on those properties.  Looking around for a solution will guide you to this article (WPF Basic Data Binding FAQ).  In this article, you’ll see a solution to this problem which is all about implementing a notification (with events) when property values change by letting your class implement the interface INotifyPropertyChanged.  The code changes to BusyState.cs conclude it as follows:

   1: namespace Behaviors101

   2: {

   3:     class BusyState : INotifyPropertyChanged

   4:     {

   5:         private bool isBusy;

   6:         private string text;

   7:  

   8:         // Declare the event

   9:         public event PropertyChangedEventHandler PropertyChanged;

  10:  

  11:         public bool IsBusy 

  12:         {

  13:             get { return isBusy; }

  14:             set {

  15:                 isBusy = value;

  16:                 // Call OnPropertyChanged whenever the property IsBusy is updated

  17:                 OnPropertyChanged("IsBusy");

  18:             }

  19:         }

  20:       

  21:         public string BusyText 

  22:         {

  23:             get { return text; } 

  24:             set {

  25:                 text = value;

  26:                 // Call OnPropertyChanged whenever the property Text is updated

  27:                 OnPropertyChanged("BusyText");

  28:             }

  29:         }

  30:  

  31:         // Create the OnPropertyChanged method to raise the event

  32:         private void OnPropertyChanged(string p)

  33:         {

  34:             PropertyChangedEventHandler handler = PropertyChanged;

  35:             if (handler != null)

  36:             {

  37:                 handler(this, new PropertyChangedEventArgs(p));

  38:             }

  39:         }

  40:     }

  41: }

 

with that, go ahead and run your application and see how that the textblock and checkbox change values every 5 seconds.

image

6. Importing the Busy State Indicator Behavior:

The previous step enabled us to test out the application busy state and making sure everything is working fine.  Of course the goal is to use a better busy state indicator UI instead of the the checkbox and textblock, so we’ll now use the BusyStateIndicator Behavior.

If you have successfully downloaded the behavior from the gallery, you’ll see that you have two components (3 files in total):

  • CircularProgressBar (XAML and CS files):  this is the artistic work which shows a bar of circles moving in a circular fashion.  This is built by Sasha Barber and more information can be obtained here.

image

  • BusyIndicatorBehavior (CS file): which is the behavior that uses the progress bar (notice the using Progress statement in the code).  It implements a number of dependency properties that enable you to bind to.  It also gives you control of the behavior such as dimming the application, the target visual, …  This work is by Pedro Pombeiro and more information can be obtained from here.

With that, all you need to do is to include those files into your project, and reference the BusyStateIndicator behavior namespace in XAML.  I have renamed the namespace in the imported files to Behaviors101 to make it simpler for me to reuse the previously used xmlns:local reference.

7. Using the BusyStateIndicator Behavior:

Now all files are imported to your project and referenced properly, you only need to look for the grid you want to target for the busy state behavior and attach the behavior.  Also, you need to bind the BusyState property of the behavior to the whatever tells it to go busy (BusyStateObject.IsBusy is the one in our case).

   1: <Grid

   2:    DataContext="{Binding Source={StaticResource BusyStateObject}}"

   3:    local:BusyIndicatorBehavior.BusyState="{Binding IsBusy}"

   4:    local:BusyIndicatorBehavior.TargetVisual="{Binding RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type Grid}}}">

Notice how that I have set the DataContext for data binding to the static resource BusyStateObject in line 2.  Then I have used simple binding in line 3 to bind BusyState of the behavior (the target property) to IsBusy (the source property).  In line 4, TargetVisual is bound to a relative source which is the Grid in this case.

Run the application now, and you’ll see that the circular bar fades in whenever the timer ticks, and gradually fades out on the next tick.

image

In Summary:

We have learned in this post how to use the BusyStateIndicator behavior and the attached Circular Progress Bar.  The idea was to reuse an existing behavior available from the Expression Community Gallery, and setting the stage for using it with a busy state object/flag.  A future post will utilize this behavior in a more practical way, but I wanted to detail the steps for you to use in your projects leveraging WPF and behaviors.

Behaviors in Expression Blend


This post aims at working with behaviors in Windows Presentation Foundation (WPF) via Visual Studio 2010 and Expression Blend Preview for .NET 4. I will start with the basics and follow up with posts to work on some of the details.

Behaviors are basically objects that encapsulate triggers (usually caused by external events) and actions that represent the operations to purpose the behavior. Behaviors have been introduced in Expression Blend 3 and are supported in both WPF and Silverlight to provide interactivity without code. They can be designed in Blend or coded in Visual Studio. To start off, we’re going to build a simple behavior and will code it in Visual Studio 2010 (yeah, the hard way gets you learning!). The behavior is going to be attached to a button and will have simple actions like displaying a message or changing the background.

Here are the steps to do build your first behavior:

1. Create a new Project in VS2010 – Choose WPF Application and give it a name:

I have named my project Behaviors101. One important thing to note here is to make sure you target the appropriate .NET framework based on the tools you use. For example, choose .NET Framework 3.5 with Visual Studio 2008 and Expression Blend 3. If you’re a beta savvy, then get your hands on the beta version of Visual Studio 2010, and Expression Blend Preview .NET 4. With the latter ones, make sure you target .NET 4.0 so you can open the project in both VS and Blend.

2. Add a new class file to your project ButtonBehaviors101.cs with following code:

 

   1: public class ButtonBehaviors101 : Behavior<Button>

   2: {

   3:     public ButtonBehaviors101()

   4:     {

   5:     }

   6:  

   7:     protected override void OnAttached()

   8:     {

   9:         this.AssociatedObject.Click += new RoutedEventHandler(AssociatedObject_Click);

  10:     }

  11:  

  12:     protected override void OnDetaching()

  13:     {

  14:         this.AssociatedObject.Click -= this.AssociatedObject_Click;

  15:     }

  16:  

  17:     void AssociatedObject_Click(object sender, RoutedEventArgs e)

  18:     {

  19:         MessageBox.Show("Hello from Behaviors!");

  20:     }

  21: }

 

3. Make sure of proper assembly references are in place for your new Behavior:

If you’re working against .NET 3.5, then use Microsoft.Expression.Interactions. If in .NET 4.0 then use System.Windows.Interactivity as this functionality will move to the core platform in .NET 4.0. After that, use proper using statement according to your reference.

   1: using System.Windows.Interactivity; // referenced assembly System.Windows.Interactivity 

   2: using System.Windows; 

   3: using System.Windows.Controls; 


4. Make proper references In XAML:

In order for us to wire up our simple behavior and attach it to a button, we need to reference the proper assemblies in XAML as well. Also, you need to reference your current namespace to get access to the custom class ButtonBehavior101.

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"

xmlns:local="clr-namespace:Behaviors101"

5. Wire up your XAML control to the newly created behavior:

   1: <Grid> 

   2:    <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="80,71,0,0" Name="button1" VerticalAlignment="Top" Width="75"> 

   3:       <i:Interaction.Behaviors> 

   4:          <local:ButtonBehaviors101/> 

   5:       </i:Interaction.Behaviors> 

   6:    </Button> 

   7: </Grid> 


Now, when you run your application and click the button, and you’ll see your behavior in action which basically displays a message box saying: Hello from Behaviors.

You may ask now why to behaviors? And the rational is that you can package it and make reusable (attache-able) to different objects (Buttons in our example). Also, when you decide to change the behavior then your client (the attached object) doesn’t need to change anything from his side.

So, let’s change the behavior action and proof that we don’t need to touch our main application. We’ll do that by changing the behavior to change a property of the button, instead of showing a message box. We will change the background this time.

6. Change the code of the associated click event to be the following:

   1: void AssociatedObject_Click(object sender, RoutedEventArgs e)

   2: {

   3:     //MessageBox.Show("Hello from Behaviors!");

   4:     this.AssociatedObject.Content = "Clicked!";

   5:     SolidColorBrush newBruch = new SolidColorBrush(Color.FromRgb(255,0,0));

   6:     this.AssociatedObject.Background = newBruch;

   7: }


Run the application and you’ll find that the button changing behavior based on the changes you have made to the ButtonBehavior101 class.

One interesting thing will happen as well. The button will not only change the background, but will start fading the background color in and out instead of turning it to the value assigned. This is caused by a default action triggered upon keyboard focus. To check what’s behind the scenes, we can open the project in Expression Blend, and discover that default trigger in design mode.

Behind the scenes in Blend:

1. Open the project in Expression Blend:

Make sure you use the right tools together. Blend 3 works with Visual Studio 2008 and Blend Preview for .NET 4 works with Visual Studio 2010.

2. Edit the button template to access the internals of button components:

Right-click on the button in Blend, and choose Edit Template -> Edit a Copy.

You’ll be presented with a Create Style Resource dialog, accept the defaults and click OK.

3. Access the triggers to find out that focus is behind the fading behavior:

While in the palette of the button, click on "Triggers" tab and you’ll be presented with a number of triggers. One of them is conditioned on IsKeboardFocused and the action is setting Chrome.RenderDefaulted to true. This action enables or disables the fading behavior. The question now is why it’s fading, and my guess for now is that it relates to the Windows theme in use. A subject of research in the futureJ !

In Summary

We have seen how to create a simple behavior from scratch in code, and how changing that behavior simply doesn’t require us recoding on the client that uses this behavior. Of course coding is not the optimal way, and in the future we’ll discover how to design complex behaviors in Blend without writing code.

Stay tuned for more posts.

Blog at WordPress.com.
Theme: Esquire by Matthew Buchanan.

Follow

Get every new post delivered to your Inbox.