Introduction
This post describes a class that can be used to reduce the amount of redundant code needed to write a standard WPF desktop application. The class adds single instance support, easier unhandled exception handling, and can handle missing assemblies. The class is an extension of the App.xaml file, so can still be edited using Visual Studio's XAML editor.
This article is also described at my site.
Background
With the types of applications that I develop, I usually prefer to make my programs single instance applications. Essentially, this means ensuring that only one instance of the application is running at any given time, and all subsequent application opening requests are handled by the single instance already running in memory.
I also use many third party libraries which require external DLLs to be present. Although tools such as ILMerge are a great convenience to remove dependencies of these required external files by combining them all into a single file, due to some license restrictions and other factors, it is not always possible to do so. However, relying on other external library files raises additional problems when a file is missing or is invalid. This is another issue which I must address in most of the applications that I write.
Although single instance application support and handling of missing third party libraries is not a difficult problem to solve, it is something which I find myself writing time and time again. For this reason, I set out to create a generic application wrapper class for WPF to help reduce the amount of redundant code I write.
The current version of WPF doesn’t have any built in support for single instance applications, but there are many different ways to rectify the problem. Some examples are by using GetProcessesByName
, by using the Microsoft.VisualBasic
namespace, or by using mutexes.
For my application class, I chose to use the Microsoft.VisualBasic method, as described in the book "Pro WPF in C# 2008". This solution seemed to be the most reliable and easiest method to implement in a generic manner.
.NET applications already support missing assembly issues by the use of the AppDomain.CurrentDomain.AssemblyResolve
event, so it was just a simple matter of registering to the event and providing a wrapper for the function in the new Application
class.
In WPF applications, the App.xaml file provides an easy way to control program settings.
<Application x:Class="WpfApplicationSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Because I wanted this new Application
class to be added to any WPF project with ease, it was a requirement for the new class to be compatible with the XAML editor. I wanted the end result to look something like the following:
<local:Application x:Class=" MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
StartupUri="Window1.xaml">
<local:WpfApplication.Resources>
</local:WpfApplication.Resources>
</local:WpfApplication>
To achieve this functionality, I decided to create a wrapper for the System.Windows.Application
class which had the exact same functionality, plus the extra features. I named this class WpfApplication
. I created an empty class which derives from System.Windows.DependencyObject
, and holds a System.Windows.Application
instance in memory in order to perform all of its required functions. This System.Windows.Application
instance lives for the entire duration of the application. An event handler is attached to each event of the System.Windows.Application
instance in order to raise WpfApplication
’s events and to execute the new custom functionality for some events such as exception handling.
The WpfApplication
’s Run
methods are where the real logic starts. I created another application wrapper that derives from the single instance supporting the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
object. This object is created on each call to a Run
method, and then calls the System.Windows.Application
Run
methods.
private int Start(Window window)
{
var wrapper = new
SingleInstanceApplicationWrapper(this, IsSingleInstance) {Window = window};
wrapper.Run(Environment.GetCommandLineArgs());
return wrapper.ReturnCode;
}
Once System.Windows.Application
terminates, so does the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
wrapper. The Microsoft.VisualBasic.ApplicationServices. WindowsFormsApplicationBase.OnStartupNextInstance
method is overridden in the SingleInstanceApplicationWrapper
class so that it can then call the new event of the WpfApplication
instance.
protected override void OnStartupNextInstance(
Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs eventArgs)
{
ReadOnlyCollection<string> argsCollection = eventArgs.CommandLine;
int count = argsCollection.Count - 1;
string[] args;
if (count > 0)
{
args = new string[eventArgs.CommandLine.Count - 1];
for (int i = 0; i < count; ++i)
{
args[i] = argsCollection[i + 1];
}
}
else
{
args = new string[0];
}
_app.OnStartupNextInstance(new StartupNextInstanceEventArgs(args));
}
The end class looks like the following:
Using the code
In order to use the new class, we just need to copy the WpfApplication
class into our project, and change the App.xaml to use the new class instead of the default. Options can then be set as usual using Visual Studio’s XAML editor.
<local:WpfApplication x:Class="WpfApplicationSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplicationSample"
StartupUri="Window1.xaml" IsSingleInstance="True"
HandleUnhandledException="True"
HandleUnresolvedAssembly="True">
<local:WpfApplication.Resources>
</local:WpfApplication.Resources>
</local:WpfApplication>
The Microsoft.VisualBasic
reference will also need to be added to the project.
In order to complete the process, the App.xaml code will need to be changed to derive from WpfApplication
. So change from:
public partial class App : Application
{
}
to this:
public partial class App
{
}
Since App.xaml is a partial class whose base type is declared in the XAML file, it does not need to be declared again in the C# file (although it is perfectly acceptable to do so).
And that’s all there is to it. Just copy the new file to your project, change the App.xaml files, and you now have an easy way to mange a single instance application which has better support for runtime errors. See the demo project for more information.
Known bugs
There is a bug in Visual Studio 2008 where the editor displays an error such as "Could not create an instance of type 'WpfApplication'". I have been unable to determine why this bug occurs, and have come to the conclusion that it is a bug with the Visual Studio IDE, not the code itself. The error can be safely ignored without any concern (it is not a compile or runtime error, just a design time error). If anybody knows of a workaround for this, or why this occurs, then please let me know.
History
- Version 1.0 (18/12/2009) - Added to CodeProject.