Exception Handlers for Silverlight 2.0
Whereas Themelia is my foundation for all .NET development, it’s a little known fact that I have an internal build of it specifically used for Silverlight. Among the many features of Themelia for Silverlight (hereafter called “ThemeliaSL”) is something I call Exception Handlers. This isn’t the same thing as the “catch” block of a try/catch. No, it’s a concept specific to Silverlight. Also, this is a fairly discrete feature that you can implement in your own Silverlight applications without the need of my full framework. However, before you can understand this feature, you need to understand Silverlight currently works with unhandled exceptions.
When Silverlight throws an exception that’s unhandled, it eventually gets bubbled up to your System.Windows.Application instance. If you are handling the UnhandledException event of this class, then you have an opportunity to handle this exception on your own. When your UnhandledException event handler is called, you are passed an instance of System.Windows.ApplicationUnhandledExceptionEventArgs. In this class you are provided a property named ExceptionObject of type System.Exception which holds the actual exception object that has been thrown. There’s one other property though: Handled. This property is a boolean which provides you with the ability to say whether you handled the exception or not. If you set this property to true, then you are telling Silverlight that the problem has been taken care of. If you set it to false, then you are telling Silverlight that it’s about to die.
That’s really all there is to Silverlight exception handling. You can, however, make things a bit more streamlined. Say, for example, you know of various types of exceptions that your application or underlying frameworks will throw. You also know that you want to handle some of them in a specific way. Or maybe, you just don’t want any communications exceptions to ever be sent back to the client. For situations like these, you can use the concept I refer to as exception handlers.
Before we implement this concept, lets see how it will be used first:
public class Application : Themelia.Windows.Application { public Application() : base() { AddExceptionHandler(typeof(CommunicationException), delegate(Exception ex) { if (ex.InnerException == null) { ReportExceptionToFirebug(ex.InnerException); return true; } //+ return false; }); } }
Essentially, call the base constructor of the Application class we are about to create. Then, for each exception that you want to handle, register it with the AddExceptionHandler method, sending it the type of the exception and a delegate of type Func<Exception, Boolean>. In this example, when an unhandled exception of type CommunicationException is thrown, we automatically check to see if it has an inner exception. If so, we report the exception in a special way and mark the exception as handled.
You could also add more exception handlers. Here’s another example:
AddExceptionHandler(typeof(ArgumentException), delegate(Exception ex) { ReportValidationError(ex); return true; });
While this example is a rather silly one (because validation shouldn’t throw exceptions!), it does go to show you this is essentially a declarative model for exception handling. Now it’s time to move on to build the thing.
First, create your own abstract version of System.Windows.Application. This is typically a good idea anyways. It allows you to control your applications in a very streamlined manner. In this class, add the following static member:
private static Dictionary<Type, Func<Exception, Boolean>> HandlerRegistry = new Dictionary<Type, Func<Exception, Boolean>>();
This represents a registry of all exceptions handlers registered to your application. It’s a dictionary with the key representing the Type of the exception and the value representing a delegate to be called when that exception is raised.
You’ll also need a standard .NET locking object:
private static Object _lock = new Object();
Now add the following method:
//- #AddExceptionHandler -// protected void AddExceptionHandler(Type type, Func<Exception, Boolean> handler) { lock (_lock) { HandlerRegistry.Add(type, handler); } }
This will allow you to register exception handlers from your concrete instance of your custom application class.
Now it’s on to the meat of the implementation. In your base class application class constructor, handle the UnhandledException event.
this.UnhandledException += new EventHandler<System.Windows.ApplicationUnhandledExceptionEventArgs>(OnUnhandledException);
Then, in the method that will be called upon the event being raised, do something like this:
//- #OnUnhandledException -// protected virtual void OnUnhandledException(Object sender, ApplicationUnhandledExceptionEventArgs ea) { lock (_lock) { if (HandlerRegistry.Count > 0) { Exception exception = ea.ExceptionObject; Type type = exception.GetType(); if (HandlerRegistry.ContainsKey(type)) { ea.Handled = HandlerRegistry[type](exception); } } } if (!System.Diagnostics.Debugger.IsAttached && this.AutoReportErrorToDOM && !ea.Handled) { ea.Handled = true; Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(ea); }); } }
Essentially, if there are any registered handlers, check to see if the currently thrown exception is one of those exception registered to be handled. If so, then pull the delegate from the registry and call it.
Notice that the delegate that we registered is Func<Exception, Boolean>. Functionally, this means is that the delegate will accept an Exception object and return a boolean. Interpreted, this means that in the delegate you register you need to return a boolean representing whether you want the exception handled or not.
You may also notice in the above code that there is a property named AutoReportErrorToDOM. This does exactly what it says and it’s nothing too exciting:
//- @AutoReportErrorToDOM -// public Boolean AutoReportErrorToDOM { get; set; }
The ReportErrorToDOM method is the same one that Visual Studio 2008 throws in with the Silverlight template. For completeness, here it is:
//- #ReportErrorToDOM -// protected void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs ea) { try { String errorMsg = ea.ExceptionObject.Message + ea.ExceptionObject.StackTrace; errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n"); System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight 2 Application " + errorMsg + "\");"); } catch (Exception) { } }
At this point, you have everything you need to make the samples that we went through work properly. Just be sure to base your application class on the new one and you’ll be set.
One important note is that when you are using custom visual entities in Silverlight, you need to make sure that you properly register the namespace in XAML. For example, say the above class was called “Application” in the “Themelia.Windows” CLR namespace in the “Themelia.Silverlight” assembly. For this configuration, you would use the following xaml:
<t:Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:t="clr-namespace:Themelia.Windows;assembly=Themelia.Silverlight" x:Class="Sample.Application" />
With this, you’re all set.