Global BusyIndicator for DomainContext

Just a short post to help out some poor soul dealing with some of the same issues I did before it hit me like a cream pie (right in the face).

The Scenario:

I’ve got a Silverlight 4 LOB Application, that will need to heavily use a BusyIndicator during loading operations (when I cant do them in the background) to keep users from clicking things and getting confused.

When I fired up the application months and months ago, I had the foresight to include the busy indicator in a global accessible place to use it as part of the main application framework.

If you have not done this before, you can implement it as follows:

Open your App.xaml.cs file inside your project.  The overall idea is pretty simple.  Rather than loading a “Grid” into the Silverlight object container, we are going to load a Busy Indicator, and then put our main loading page inside that.  Here’s how:

First, lets wire up an BusyIndicator

public partial class App : Application
    {
        private BusyIndicator progressIndicator;

 

There are several ways you could do this, this is just the way I chose to.  You’ll need to make sure you have a reference to, and a using statement to the following assembly

using System.Windows.Controls;

Once we have our indicator wired up, it’s pretty easy to shift things around a little, and load that into a reserved property setup for us by the nice Silverlight Developers, called the “RootVisual”

public App()
{
      this.Startup += this.Application_Startup;
           
      InitializeComponent();
}
private void Application_Startup(object sender, StartupEventArgs e)
{
        InitializeRootVisual();            
}
 protected virtual void InitializeRootVisual(bool isStartup)
{
            this.progressIndicator = new BusyIndicator();
            
            this.Resources.Add("MainProgress", this.progressIndicator);
            this.progressIndicator.HorizontalAlignment = HorizontalAlignment.Stretch;
            this.progressIndicator.VerticalAlignment = VerticalAlignment.Stretch;

            //Replace new MainPage() with the name of your starting xaml page;
            this.progressIndicator.Content = new MainPage();
        

            this.RootVisual = this.progressIndicator;
 }

Ok, so now that we have this wired up to use, you can test its working by setting the progressIndicator.IsBusy property to either “true” or “false” to toggle it, and see it running in application. 

At this point though, it’s far from useful, we need it to be semi aware of what’s going on, when it needs to show, and when it should go away.

Wiring it to your domain context:

If your application is like mine, I’m not using any fancy context managers, or factory context handlers giving me an operating instance.  Rather I instantiate a context in a page when I need to do something, and terminate and dispose it when I’m done.  To keep hold of what’s going on with my context on the client side, I monitor calls coming into the server and make sure none of them go errant, but thats another post, and that does not help a client side progress bar.  So what do we do?  Good question:

The "reference” that we get through generated code, is lucky made partial so we are able to get our fingers into it on the client side.  We can’t do a whole lot, but we can do what we need to do:

So here’s how I got started.  In my Client Side (Silverlight) project, I create a new class with the same name as my server side context.  In this case we’ll refer to it as “MyDomainContext”

using System;
using System.Windows.Controls;
using IQ.Client;

namespace My.ServerProject.Services
{
    public partial class MyDomainContext
    {
        BusyIndicator busy = (BusyIndicator)App.Current.RootVisual;
        public override System.ServiceModel.DomainServices.Client.LoadOperation Load(System.ServiceModel.DomainServices.Client.EntityQuery query, System.ServiceModel.DomainServices.Client.LoadBehavior loadBehavior, Action<System.ServiceModel.DomainServices.Client.LoadOperation> callback, object userState)
        {
            this.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(MyDomainContext_PropertyChanged);
            this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MyDomainContext_PropertyChanged);
              
            return base.Load(query, loadBehavior, callback, userState);
        }

        void MyDomainContext_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsLoading" && this.IsLoading == true)
            {
                busy.IsBusy = true;
            }
            else if (e.PropertyName == "IsLoading" && this.IsLoading == false)
            {
                busy.IsBusy = false;
            }
        }
    }
}

Keep in mind, this is ALL on the client side.  Dont forget to change your namespace to the server side namespace.  If you’re a little confused as to what’s going on, don’t feel bad.  It took me a minute to wrap my head around it too.  You are NOT sharing a partial class across a service boundary. 

This works because of the Proxy class generated code that having a RIA services project throws into the Silverlight application.  The partial class gets generated in a hidden file, and we are going to extend the partial.

You can see by the class extension, that we are waiting for a load operation to be called, and then tapping into the PropertyChanged Callback.  We remove an existing reference of the callback and wire up a new one so we don’t keep adding callbacks on each subsequent call.

Note:

This is just a quick example external to my actual running project, I am well aware that adding and removing event handlers like that will not work when there is more than one calling thread…etc.  Use this to create your own implementation that works in your scenario.

Good Luck!

Bryan Sampica

No Comments