WPF 4.5 Application and Windows

Exclusive offer: get 50% off this eBook here
Windows Presentation Foundation 4.5 Cookbook

Windows Presentation Foundation 4.5 Cookbook — Save 50%

Over 80 recipes to effectively and efficiently develop rich Windows client applications on the Windows platform with this book and ebook.

£22.99    £11.50
by Pavel Yosifovich | September 2012 | Cookbooks Enterprise Articles

Any attempt at mastering a technology, any technology, requires a good understanding of its foundations. This understanding makes it possible to grasp the more complex aspects of that technology; Windows Presentation Foundation ( WPF) is no different. In this article, we'll take a broader look at WPF's application model, including the use of windows within an application.

In this article by Pavel Yosifovich, author of Windows Presentation Foundation 4.5 Cookbook, we will cover:

  • Creating a window
  • Creating a dialog box
  • Using the common dialog boxes
  • Creating ownership between windows
  • Creating a custom shaped window
  • Creating a single instance application
  • Handling an unhandled exception

(For more resources on this topic, see here.)

Creating a window

Windows are the typical top level controls in WPF. By default, a MainWindow class is created by the application wizard and automatically shown upon running the application. In this recipe, we'll take a look at creating and showing other windows that may be required during the lifetime of an application.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a new class derived from Window and show it when a button is clicked:

  1. Create a new WPF application named CH05.NewWindows.
  2. Right-click on the project node in Solution explorer, and select Add | Window…:
  3. In the resulting dialog, type OtherWindow in the Name textbox and click on Add.
  4. A file named OtherWindow.xaml should open in the editor. Add a TextBlock to the existing Grid, as follows:

    <TextBlock Text="This is the other window" FontSize="20"
       VerticalAlignment="Center" HorizontalAlignment="Center" />

  5. Open MainWindow.xaml. Add a Button to the Grid with a Click event handler:

    <Button Content="Open Other Window" FontSize="30"
            Click="OnOpenOtherWindow" />

  6. In the Click event handler, add the following code:

    void OnOpenOtherWindow(object sender, RoutedEventArgs e) {
       var other = new OtherWindow();
       other.Show();
    }

  7. Run the application, and click the button. The other window should appear and live happily alongside the main window:
  8. How it works...

    A Window is technically a ContentControl, so can contain anything. It's made visible using the Show method. This keeps the window open as long as it's not explicitly closed using the classic close button, or by calling the Close method. The Show method opens the window as modeless—meaning the user can return to the previous window without restriction. We can click the button more than once, and consequently more Window instances would show up.

    There's more...

    The first window shown can be configured using the Application.StartupUri property, typically set in App.xaml. It can be changed to any other window. For example, to show the OtherWindow from the previous section as the first window, open App.xaml and change the StartupUri property to OtherWindow.xaml:

    StartupUri="OtherWindow.xaml"
    

    Selecting the startup window dynamically

    Sometimes the first window is not known in advance, perhaps depending on some state or setting. In this case, the StartupUri property is not helpful. We can safely delete it, and provide the initial window (or even windows) by overriding the Application.OnStartup method as follows (you'll need to add a reference to the System.Configuration assembly for the following to compile):

    protected override void OnStartup(StartupEventArgs e) {
       Window mainWindow = null;
       // check some state or setting as appropriate
             if(ConfigurationManager.AppSettings["AdvancedMode"] == "1")
          mainWindow = new OtherWindow();
       else
          mainWindow = new MainWindow();
       mainWindow.Show();
    }

    This allows complete flexibility in determining what window or windows should appear at application startup.

    Accessing command line arguments

    The WPF application created by the New Project wizard does not expose the ubiquitous Main method. WPF provides this for us – it instantiates the Application object and eventually loads the main window pointed to by the StartupUri property.

    The Main method, however, is not just a starting point for managed code, but also provides an array of strings as the command line arguments passed to the executable (if any). As Main is now beyond our control, how do we get the command line arguments?

    Fortunately, the same OnStartup method provides a StartupEventArgs object, in which the Args property is mirrored from Main. The downloadable source for this chapter contains the project CH05.CommandLineArgs, which shows an example of its usage. Here's the OnStartup override:

    protected override void OnStartup(StartupEventArgs e) {
       string text = "Hello, default!";
       if(e.Args.Length > 0)
          text = e.Args[0];
     
       var win = new MainWindow(text);
       win.Show();
    }

    The MainWindow instance constructor has been modified to accept a string that is later used by the window. If a command line argument is supplied, it is used.

Creating a dialog box

A dialog box is a Window that is typically used to get some data from the user, before some operation can proceed. This is sometimes referred to as a modal window (as opposed to modeless, or non-modal). In this recipe, we'll take a look at how to create and manage such a dialog box.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a dialog box that's invoked from the main window to request some information from the user:

  1. Create a new WPF application named CH05.Dialogs.
  2. Add a new Window named DetailsDialog.xaml (a DetailsDialog class is created).
  3. Visual Studio opens DetailsDialog.xaml. Set some Window properties: FontSize to 16, ResizeMode to NoResize, SizeToContent to Height, and make sure the Width is set to 300: ResizeMode="NoResize" SizeToContent="Height" Width="300" FontSize="16"
  4. Add four rows and two columns to the existing Grid, and add some controls for a simple data entry dialog as follows:

    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBlock Text="Please enter details:" Grid.ColumnSpan="2"
      Margin="4,4,4,20" HorizontalAlignment="Center"/>
    <TextBlock Text="Name:" Grid.Row="1" Margin="4"/>
    <TextBox Grid.Column="1" Grid.Row="1" Margin="4"
             x:Name="_name"/>
    <TextBlock Text="City:" Grid.Row="2" Margin="4"/>
    <TextBox Grid.Column="1" Grid.Row="2" Margin="4"
             x:Name="_city"/>
    <StackPanel Grid.Row="3" Orientation="Horizontal"
                Margin="4,20,4,4" Grid.ColumnSpan="2"
                HorizontalAlignment="Center"> <Button Content="OK" Margin="4"  />
        <Button Content="Cancel" Margin="4" />
    </StackPanel>

  5. This is how it should look in the designer:

  6. The dialog should expose two properties for the name and city the user has typed in. Open DetailsDialog.xaml.cs. Add two simple properties:

    public string FullName { get; private set; }
    public string City { get; private set; }

  7. We need to show the dialog from somewhere in the main window. Open MainWindow.xaml, and add the following markup to the existing Grid:
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Button Content="Enter Data" Click="OnEnterData"
            Margin="4" FontSize="16"/>
    <TextBlock FontSize="24" x:Name="_text" Grid.Row="1"
        VerticalAlignment="Center" HorizontalAlignment="Center"/>
  8. In the OnEnterData handler, add the following:

    private void OnEnterData(object sender, RoutedEventArgs e) {
       var dlg = new DetailsDialog();
       if(dlg.ShowDialog() == true) {
          _text.Text = string.Format(
              "Hi, {0}! I see you live in {1}.",   
              dlg.FullName, dlg.City);
       }
    }

  9. Run the application. Click the button and watch the dialog appear. The buttons don't work yet, so your only choice is to close the dialog using the regular close button. Clearly, the return value from ShowDialog is not true in this case.
  10. When the OK button is clicked, the properties should be set accordingly. Add a Click event handler to the OK button, with the following code:

    private void OnOK(object sender, RoutedEventArgs e) {
       FullName = _name.Text;
       City = _city.Text;
       DialogResult = true;
       Close();
    }

    The Close method dismisses the dialog, returning control to the caller. The DialogResult property indicates the returned value from the call to ShowDialog when the dialog is closed.

  11. Add a Click event handler for the Cancel button with the following code:

    private void OnCancel(object sender, RoutedEventArgs e) {
       DialogResult = false;
       Close();
    }

  12. Run the application and click the button. Enter some data and click on OK:

  13. You will see the following window:

  14. How it works...

    A dialog box in WPF is nothing more than a regular window shown using ShowDialog instead of Show. This forces the user to dismiss the window before she can return to the invoking window. ShowDialog returns a Nullable (can be written as bool? in C#), meaning it can have three values: true, false, and null. The meaning of the return value is mostly up to the application, but typically true indicates the user dismissed the dialog with the intention of making something happen (usually, by clicking some OK or other confirmation button), and false means the user changed her mind, and would like to abort. The null value can be used as a third indicator to some other application-defined condition.

    The DialogResult property indicates the value returned from ShowDialog because there is no other way to convey the return value from the dialog invocation directly. That's why the OK button handler sets it to true and the Cancel button handler sets it to false (this also happens when the regular close button is clicked, or Alt + F4 is pressed).

    Most dialog boxes are not resizable. This is indicated with the ResizeMode property of the Window set to NoResize. However, because of WPF's flexible layout, it certainly is relatively easy to keep a dialog resizable (and still manageable) where it makes sense (such as when entering a potentially large amount of text in a TextBox – it would make sense if the TextBox could grow if the dialog is enlarged).

    There's more...

    Most dialogs can be dismissed by pressing Enter (indicating the data should be used) or pressing Esc (indicating no action should take place). This is possible to do by setting the OK button's IsDefault property to true and the Cancel button's IsCancel property to true. The default button is typically drawn with a heavier border to indicate it's the default button, although this eventually depends on the button's control template.

    If these settings are specified, the handler for the Cancel button is not needed. Clicking Cancel or pressing Esc automatically closes the dialog (and sets DiaglogResult to false). The OK button handler is still needed as usual, but it may be invoked by pressing Enter, no matter what control has the keyboard focus within the Window. The CH05.DefaultButtons project from the downloadable source for this chapter demonstrates this in action.

    Modeless dialogs

    A dialog can be show as modeless, meaning it does not force the user to dismiss it before returning to other windows in the application. This is done with the usual Show method call – just like any Window. The term dialog in this case usually denotes some information expected from the user that affects other windows, sometimes with the help of another button labelled "Apply".

    The problem here is mostly logical—how to convey the information change. The best way would be using data binding, rather than manually modifying various objects. We'll take an extensive look at data binding in the next chapter.

Using the common dialog boxes

Windows has its own built-in dialog boxes for common operations, such as opening files, saving a file, and printing. Using these dialogs is very intuitive from the user's perspective, because she has probably used those dialogs before in other applications. WPF wraps some of these (native) dialogs. In this recipe, we'll see how to use some of the common dialogs.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a simple image viewer that uses the Open common dialog box to allow the user to select an image file to view:

  1. Create a new WPF Application named CH05.CommonDialogs.
  2. Open MainWindow.xaml. Add the following markup to the existing Grid:
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Button Content="Open Image" FontSize="20" Click="OnOpenImage"
            HorizontalAlignment="Center" Margin="4" />
    <Image Grid.Row="1" x:Name="_img" Stretch="Uniform" />
  3. Add a Click event handler for the button. In the handler, we'll first create an OpenFileDialog instance and initialize it (add a using to the Microsoft.Win32 namespace):

    void OnOpenImage(object sender, RoutedEventArgs e) {
       var dlg = new OpenFileDialog {
          Filter = "Image files|*.png;*.jpg;*.gif;*.bmp",
          Title = "Select image to open",
          InitialDirectory = Environment.GetFolderPath(
            Environment.SpecialFolder.MyPictures)
       };

  4. Now we need to show the dialog and use the selected file (if any):

    if(dlg.ShowDialog() == true) {
       try {
          var bmp = new BitmapImage(new Uri(dlg.FileName));
          _img.Source = bmp;
       }
       catch(Exception ex) {
          MessageBox.Show(ex.Message, "Open Image");
       }
    }

  5. Run the application. Click the button and navigate to an image file and select it. You should see something like the following:

How it works...

The OpenFileDialog class wraps the Win32 open/save file dialog, providing easy enough access to its capabilities. It's just a matter of instantiating the object, setting some properties, such as the file types (Filter property) and then calling ShowDialog. This call, in turn, returns true if the user selected a file and false otherwise (null is never returned, although the return type is still defined as Nullable for consistency).

The look of the Open file dialog box may be different in various Windows versions. This is mostly unimportant unless some automated UI testing is done. In this case, the way the dialog looks or operates may have to be taken into consideration when creating the tests.

The filename itself is returned in the FileName property (full path). Multiple selections are possible by setting the MultiSelect property to true (in this case the FileNames property returns the selected files).

There's more...

WPF similarly wraps the Save As common dialog with the SaveFileDialog class (in the Microsoft.Win32 namespace as well). Its use is very similar to OpenFileDialog (in fact, both inherit from the abstract FileDialog class).

What about folder selection (instead of files)? The WPF OpenFileDialog does not support that. One solution is to use Windows Forms' FolderBrowseDialog class. Another good solution is to use the Windows API Code Pack described shortly.

Another common dialog box WPF wraps is PrintDialog (in System.Windows.Controls). This shows the familiar print dialog, with options to select a printer, orientation, and so on. The most straightforward way to print would be calling PrintVisual (after calling ShowDialog), providing anything that derives from the Visual abstract class (which include all elements). General printing is a complex topic and is beyond the scope of this book.

What about colors and fonts?

Windows also provides common dialogs for selecting colors and fonts. However, these are not wrapped by WPF. There are several alternatives:

  • Use the equivalent Windows Forms classes (FontDialog and ColorDialog, both from System.Windows.Forms)
  • Wrap the native dialogs yourself
  • Look for alternatives on the Web

The first option is possible, but has two drawbacks: first, it requires adding reference to the System.Windows.Forms assembly; this adds a dependency at compile time, and increases memory consumption at run time, for very little gain. The second drawback has to do with the natural mismatch between Windows Forms and WPF. For example, ColorDialog returns a color as a System.Drawing.Color, but WPF uses System.Windows.Media.Color. This requires mapping a GDI+ color (WinForms) to WPF's color, which is cumbersome at best.

The second option of doing your own wrapping is a non-trivial undertaking and requires good interop knowledge. The other downside is that the default color and font common dialogs are pretty old (especially the color dialog), so there's much room for improvement.

The third option is probably the best one. There are more than a few good candidates for color and font pickers. For a color dialog, for example, you can use the ColorPicker or ColorCanvas provided with the Extended WPF toolkit library on CodePlex (http://wpftoolkit.codeplex.com/). Here's how these may look (ColorCanvas on the left-hand side, and one of the possible views of ColorPicker on the right-hand side):

The Windows API Code Pack

The Windows API Code Pack is a Microsoft project on CodePlex (http://archive.msdn.microsoft.com/WindowsAPICodePack) that provides many .NET wrappers to native Windows features, in various areas, such as shell, networking, Windows 7 features (this is less important now as WPF 4 added first class support for Windows 7), power management, and DirectX. One of the Shell features in the library is a wrapper for the Open dialog box that allows selecting a folder instead of a file. This has no dependency on the WinForms assembly.

Windows Presentation Foundation 4.5 Cookbook Over 80 recipes to effectively and efficiently develop rich Windows client applications on the Windows platform with this book and ebook.
Published: September 2012
eBook Price: £22.99
Book Price: £36.99
See more
Select your format and quantity:

(For more resources on this topic, see here.)

Creating ownership between windows

Window objects are self-sufficient by default, and are independent of other windows in the application. Sometimes, however, it's useful to connect two (or more) windows in an owner-owned relationship. An owned window obeys the following rules:

  • Closed automatically if its owner is closed
  • Minimized automatically if its owner is minimized
  • Always appears on top of its owner, but unconstrained to its surface (unlike traditional Win32 child windows)
  • Never shown in the task bar if it's currently minimized

In this recipe, we'll see how to create such ownership and show a typical use case.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a tool-like window that is owned by the main window, demonstrating a typical usage of window ownership:

  1. Create a new WPF Application project named CH05.OwnerWindows.
  2. Right-click on the project node in Solution Explorer and select Add | Window….
  3. Type ToolsWindow in the Name text box and click on Add.
  4. The ToolsWindow.xaml file should be open. Set the following properties on the Window
    object:SizeToContent="WidthAndHeight" ResizeMode="NoResize"
  5. Replace the existing Grid with a ToolBar and add some buttons as follows:
    <ToolBar FontSize="20">
        <RadioButton Content="Pointer" Margin="4"
                     IsChecked="True"/>
        <RadioButton Content="Pencil" Margin="4"/>
        <RadioButton Content="Brush" Margin="4"/>
        <RadioButton Content="Eraser" Margin="4"/>
        <RadioButton Content="Selection" Margin="4"/>
    </ToolBar>
  6. Open App.xaml. Remove the StartupUri property value from the Application object.
  7. Open App.xaml.cs. Override the OnStartup method as follows:

    protected override void OnStartup(StartupEventArgs e) {
       var mainWindow = new MainWindow();
       var toolWindow = new ToolsWindow();
       mainWindow.Show();
       toolWindow.Owner = mainWindow;
       toolWindow.Show();
    }

  8. Run the application. Note that the Tool window is always on top of the main window. Minimize the main window – the tool window is minimized as well. Restore it – both windows are restored.

How it works...

Window ownership is not a WPF specific feature – it's a capability exposed by the Win32 user API. WPF simply makes it easily accessible.

Every Window object has an Owner property. By default, it's null, meaning the Window is unowned, independent of other windows. If an owner is set, the Window now obeys ownership rules, as described in the introduction section of this recipe.

A Window (not a NavigationWindow hosted in a browser) can be removed from the Task Bar by specifying false for the ShowInTaskBar property.

There's more...

Ownership can be removed simply by reverting the Owner property back to null, freeing the window once again.

Each Window also has an OwnedWindows property, which is a collection of that Window's owned windows (of type WindowCollection). This may be useful when some operation needs to be performed on all or some of windows owned by Window.

Creating a custom shaped window

A typical window has several aspects that are not directly controllable by a WPF application, such as the look of the title bar, minimize, maximize, and close buttons; its shape is always rectangular, and so on. These settings (called the non-client area of the window) are defined by the current Windows theme selected by the user using the Control Panel, with some customization possible for font sizes, colors, caption color, and so on, but the basic appearance characteristics of the window remain.

An application may want to customize the way a window looks from the outside. Canonical examples of this are media players. The built-in Windows Media Player, for instance, can be switched to skin mode where its shape becomes something that is far from rectangular (this particular skin was downloaded from Microsoft's website; in Windows Media Player, open the View | Skin chooser menu and click More Skins ):

Let's see how we can create a custom shaped window with WPF.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a custom shaped window and make it function just like a regular window for moving and closing:

  1. Create a new WPF Application project named CH05.CustomShapeWindow.
  2. Open MainWindow.xaml. Set the following properties of the Window object to remove the default window appearance so we can provide our own:
    AllowsTransparency="True" WindowStyle="None" Background="Transparent"
  3. At this point, the window is pretty much invisible. It's time to provide some irregular content. Add the following markup in the existing Grid:

    <Rectangle RadiusX="30" RadiusY="30">
        <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0,1">
                <GradientStop Color="DarkBlue" Offset="0" />
                <GradientStop Color="#80000080" Offset="1" />
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <TextBlock TextAlignment="Center" VerticalAlignment="Top"
        Margin="4" Text="My Window Title" FontSize="18"
        Foreground="White" FontWeight="Bold" />
    <Button Content="X" HorizontalAlignment="Right"
        FontWeight="Bold" VerticalAlignment="Top"
        Margin="20,4" FontSize="16" />
    <TextBlock Text="Welcome to the new Window!"
        Foreground="Yellow" FontSize="25"
        VerticalAlignment="Center" HorizontalAlignment="Center" />

  4. Running the application now shows the following:

    The window cannot be moved with the mouse (but can be moved by pressing Alt + Space and selecting Move), and cannot be closed with the mouse (but can be closed with Alt + F4 or Alt + Space and then Close).

  5. To make the window work as expected with the mouse, we'll add two event handlers.
  6. The first is a Click handler for the "X" button. Enter the following in the handler:

    private void OnClose(object sender, RoutedEventArgs e) {
       Close();
    }

  7. The second is a MouseLeftButtonDown handler for the Grid. Add the following code in the handler:

    private void OnMove(object sender, MouseButtonEventArgs e) {
       DragMove();
    }

  8. Run the application. You should be able to move the window with the mouse and close it by clicking the button.

How it works...

The AllowsTransparency and WindowStyle settings shown above are the mandatory ingredients that tell windows not to paint anything in the so-called non-client area of the window. Setting Background to Transparent allows other content to show through and flesh out the real look of the window.

In the preceding XAML, a Rectangle with rounded corners is placed first, followed by two TextBlock instances, one for some kind of title and the other to simulate the actual content of the window. A Button is added as well, to rovide a convenient way to close the window.

One consequence of the title bar removal is losing the ability to move or close the window with the mouse. Usually such features are desirable, so we need to implement them ourselves. Fortunately, this is not difficult. To close the window, we just wire up some control (in this case a Button) to call the regular Window.Close method. Moving the window seems more complex, and technically it is, but WPF makes this easy with the Window.DragMove method. We just need to call it in a MouseLeftButtonDown event handler; the rest is done for us as part of DragMove.

There's more...

What about custom shaped windows that look like the previous Media Player skin? The trick here is to use an image with transparency (typically a PNG file) as the window's background. The downloadable source for this chapter contains a project named CH05.ImageShapeWindow that shows this in action. This is the required XAML to make this work (apart from the two required property settings):

<Window.Background>
    <ImageBrush ImageSource="invader.png" />
</Window.Background>

The invader.png is an image with transparent areas. This is how the window looks when running:

We can add the ability to move and close the window in much the same way as previously shown (check out the source of this example).

What about reusability?

The above examples are fine for a single window that is required to be different. What if we wanted all of an application's windows to have a unique shape? Placing the same XAML and event handlers is not very reusable and becomes a maintenance headache.

A better approach would be to create a custom control template for a window and derive a new class from Window that would handle closing and moving, but potentially also minimizing and restoring. This custom class would be able to expose other special properties if needed.

Windows Presentation Foundation 4.5 Cookbook Over 80 recipes to effectively and efficiently develop rich Windows client applications on the Windows platform with this book and ebook.
Published: September 2012
eBook Price: £22.99
Book Price: £36.99
See more
Select your format and quantity:

(For more resources on this topic, see here.)

Creating a single instance application

We may sometimes want to limit the number of running instances of some application to just one. Running some executable creates a new Windows process to host that executable, with its own address space, tables, resources, and so on. Sometimes this is not desirable; if the executable tries to run while another instance is already running, it should quit and optionally make the other instance's main window active. A canonical example in Windows is Windows Media Player. An attempt to open a second media player activates the first Media Player window (although it's debatable whether such behavior is desired for Media Player).

Let's see how this can be achieved.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a simple application that will not run with more than one process. A second run will transfer activation to the running instance's window,and quit:

  1. Create a new WPF Application project named CH05.SingleInstApp.
  2. Open MainWindow.xaml. Give the window a distinct Title, such as Single Instance.
  3. Open App.xaml.cs. We'll need to use some Win32 functions. Add the following to the App class:

    [DllImport("user32", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindow(string cls, string win);
    [DllImport("user32")]
    static extern IntPtr SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32")]
    static extern bool IsIconic(IntPtr hWnd);
    [DllImport("user32")]
    static extern bool OpenIcon(IntPtr hWnd);

  4. Override the OnStartup method as follows:

    bool isNew;
    var mutex = new Mutex(true, "MySingleInstMutex", out isNew);
    if(!isNew) {
       ActivateOtherWindow();
       Shutdown();
    }

  5. Add the implementation of ActivateOtherWindow:

    Private static void ActivateOtherWindow() {
       var other = FindWindow(null, "Single Instance");
       if(other != IntPtr.Zero) {
          SetForegroundWindow(other);
          if(IsIconic(other))
             OpenIcon(other);

  6. Run the application without the debugger ( Ctrl + F5 or Debug | Start Without Debugging menu). The main window should appear. Now run the application again you can also use Windows Explorer). The previous window should activate. If it was minimized, it should be restored and activated.

How it works...

A Mutex is a synchronization object, typically used to synchronize access to a shared resource (such as a file), so that only one thread can access the resource at a time, thus preventing data corruption or other inconsistencies within the shared resource. Under the covers it wraps a Win32 Mutex object (represented as a handle), so it's a true kernel object, capable of cross AppDomain and cross process synchronization— something that the roughly equivalent Monitor. Enter/Exit (or the C# lock keyword) cannot do.

In this case the Mutex is not used because of its synchronization capabilities, but simply as a way to identify a singleton object in the user's session. The name string argument passed to its constructor should be unique, to not confuse objects with other processes (its creation may also fail as a result). The isNew returned value indicates whether this is a brand new kernel mutex or another handle to an existing one. If it already exists, then it was created by an already running application instance, so we want to kill this new instance. But before we do that we want to activate the main window of the running instance. This is where ActivateOtherWindow comes in.

ActivateOtherWindow uses the Win32 function FindWindow to look for the other window based on its Title; if found, it brings it to the foreground (SetForegroundWindow). If it's also minimized (IsIconic )—it restores it (OpenIcon).

There's more...

Locating the existing window may be tricky. In the preceding example, the title is a constant string, so that's easily located with FindWindow. What if the window title is something like "My App – somefile.dat", meaning it starts the same ("My App"), but continues with the active file the user is working on? FindWindow can't handle this.

An alternative would be to call the Win32 EnumWindows function, go over each top level window looking for the title (using the Win32 GetWindowText function), and match to the expected pattern.

In an extreme case, the title of the main window may be too unpredictable even for iterating with EnumWindows. A more robust alternative is possible by hosting a WCF service within the application (using the NetNamedPipeBinding binding). The service would expose an operation that instructs the app to activate its main window. All that would be needed now is to connect to the service (easy to do with WCF and Visual Studio) and invoke the operation (a decent treatment of WCF is well beyond the scope of this book). This gives the added benefit of providing a way to send parameters to the other running instance (such as a file name provided as a command line argument); a example of the parameters to pass are Microsoft Word (the file to open) and Media Player (the file to play). The downloadable project for this recipe includes an example WCF hosting for just this purpose.

First, define the service contract:

[ServiceContract]
interface IActivateWindow {
   [OperationContract]
   void Activate(string[] args);
}

Second, implement:

class ActivationService : IActivateWindow {
   public void Activate(string[] args) {
      var helper = new WindowInteropHelper(
         Application.Current.MainWindow);
      if(App.IsIconic(helper.Handle))
         App.OpenIcon(helper.Handle);
      App.SetForegroundWindow(helper.Handle);
      // use args...
   }
}

Check for another instance; if it exists, switch to it by calling the service. Otherwise, we're the first, so open a WCF host to listen for clients (all in the App class):

const string _pipeAddress =
   "net.pipe://127.0.0.1/pipe/activation";
 
protected override void OnStartup(StartupEventArgs e) {
   bool isNew;
   var mutex = new Mutex(true, "MySingleInstanceMutex",
      out isNew);
   if(!isNew) {      // use the service
      var svc = ChannelFactory<IActivateWindow>.CreateChannel(
          new NetNamedPipeBinding(),
          new EndpointAddress(_pipeAddress));
      svc.Activate(e.Args);
      Shutdown();
   }
   else {
      CreateHost();
   }
}
 
ServiceHost _host;
void CreateHost() {
   _host = new ServiceHost(typeof(ActivationService));
   _host.AddServiceEndpoint(typeof(IActivateWindow),
      new NetNamedPipeBinding(), _pipeAddress);
   _host.Open();
}

Handling an unhandled exception

If a .NET exception goes unhandled, the process crashes with an unpleasant dialog presented to the user by Windows. Unpleasantness aside, the user may lose data as a result of the crash.

WPF provides a way to catch unhandled exceptions and perhaps handle them in some meaningful way. At the very least, the application may present a friendlier looking crash report and perhaps write the exception information to some log. At best, the application may recover from the error and continue running normally. Let's see how this can be done.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a simple application that throws an unhandled exception and see how we can catch it even if it's unhandled:

  1. Create a new WPF Application project named CH05.UnhandledExceptions.
  2. Open MainWindow.xaml. Add a Button to the Grid as follows:

    <Button Content="Throw Exception" FontSize="20" Margin="10" />

  3. Add a Click handler to the button. In the handler, add the following:
    private void OnClick(object sender, RoutedEventArgs e) {
       throw new InvalidOperationException("something has gone
    wrong");
    }
  4. Running the application now without the debugger and clicking the button would crash the process, producing the default crash dialog.
  5. Open App.xaml.cs. Add an event handler for the DispatcherUnhandledException event (in the constructor):

    public App() {
       DispatcherUnhandledException += OnUnhandledException;
    }

  6. Add a using statement for the System.Diagnostics namespace.
  7. Add the following code to the handler:

    Trace.WriteLine(string.Format("{0}: Error: {1}", DateTime.Now,
        e.Exception));
    MessageBox.Show("Error encountered! Please contact support."
        + Environment.NewLine + e.Exception.Message);
    Shutdown(1);
    e.Handled = true;

  8. Run the application and click the button. You should see the following message box. Click on OK and the application is shut down without the unpleasant Windows dialog:

How it works...

The DispatcherUnhandledException event of the Application object is fired when an exception escapes handling, meaning no method on the main thread's call stack has elected to handle the exception. Without this event, the process would crash.

The event allows last minute handling of an error. If the Handled property of the DispatcherUnhandledExceptionEventArgs provided is set to true, the process does not terminate (returning to pumping messages). However, it's usually too dangerous in this situation to let the application continue running as usual, because the application may be in some inconsistent state – after all, an exception has occurred somewhere, and this may have resulted in partial work being done. It's usually safer to shut down the app and inform the user he/she should run the app again. At least the user won't see the disturbing Windows dialog and the application gets to save information about the exception and perhaps some other state before going down.

The preceding code sets Handled to true to prevent crashing, but calls Application. Shutdown to terminate the application, deemed too dangerous to continue running from this point. The call to Trace.WriteLine would show up in any configured TraceListener properties (tracing configuration is beyond the scope of this book); by default, it would go to the debugger window (if a debugger is attached). Otherwise, it can be captured by a custom tool, such as DebugView available from the SysInternals tools (http://www.sysinternals.com).

There's more...

This event can only be used to catch exceptions occurring on the UI thread (usually the main thread). Exceptions occurring on other threads (such as threads from the thread pool) will still crash the process (and not go through the event handler).

This means that letting exceptions slide to the DispatcherUnhandledException event is not generally a good idea. It should be an excepted situation that gets there – and it will never happen from a non-UI thread. Remember, this is a WPF mechanism; WPF has control of the UI thread, but not other threads. If such behavior is desired, we can use standard .NET mechanisms to be notified when an unhandled exception has occurred, such as registering with the UnhandledException event of the AppDomain class (registered on the current domain with AppDomain.CurrentDomain).


Further resources on this subject:


About the Author :


Pavel Yosifovich

Pavel Yosifovich is the CTO of CodeValue (http://www.codevalue.net), a software development, consulting, and training company, based in Israel. He writes, develops, consults, and trains developers on various software development topics, from Windows internals to .NET enterprise systems, and almost everything in between. He's a Microsoft MVP and a frequent speaker at national events.

In the past, he co-founded the startup company Quiksee that was acquired by Google in September 2010.

Books From Packt


Microsoft Silverlight 5 and Windows Azure Enterprise Integration
Microsoft Silverlight 5 and Windows Azure Enterprise Integration

Mastering LOB Development for Silverlight 5: A Case Study in Action
Mastering LOB Development for Silverlight 5: A Case Study in Action

Microsoft Silverlight 5 Data and Services Cookbook
Microsoft Silverlight 5 Data and Services Cookbook

Microsoft Silverlight 5: Building Rich Enterprise Dashboards
Microsoft Silverlight 5: Building Rich Enterprise Dashboards

MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF
MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF

Microsoft Dynamics NAV 2009 Application Design
Microsoft Dynamics NAV 2009 Application Design

Documentum 6.5 Content Management Foundations
Documentum 6.5 Content Management Foundations

OGRE 3D 1.7 Beginner's Guide
OGRE 3D 1.7 Beginner's Guide


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software