Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF

Modal dialogs in cross-platform WPF/Silverlight applications

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
18 Jun 2010CPOL6 min read 15.7K   8  
This blog post looks at the problem of showing modal dialog windows in applications that target both the Silverlight and WPF platforms. A solution is provided which allows modal dialogs to be written that work well for both technologies.

This blog post looks at the problem of showing modal dialog windows in applications that target both the Silverlight and WPF platforms. A solution is provided which allows modal dialogs to be written that work well for both technologies.

Silverlight is, roughly speaking, a subset of Windows Presentation Foundation (WPF). This means that it is possible to write applications that target both frameworks, allowing you to develop applications that work both on the desktop and on the web using the same codebase.

However, Silverlight is not a strict subset of WPF, the differences between the two frameworks can be summed up as follows:

  1. Things that WPF has that Silverlight does not
  2. Things that Silverlight has that WPF has not
  3. Things that both frameworks have, but are different!

There are a great many things that the WPF framework has that Silverlight does not, mostly due to the size constraints of Silverlight being a browser plugin.

  1. There are a few things that Silverlight has, such as Out Of Browser (OOB) support and local storage which are not in WPF because they relate to the browser context.
  2. If these areas are avoided, the creation of a cross-platform application can be straightforward, however it is point (3) that causes problems.

When developing an application which targets both frameworks, it makes sense to do most of your development primarily in Silverlight in order to avoid accidentally using WPF features. In practical terms, the way most people achieve this is to create two projects, one Silverlight, one WPF. The WPF project will include links to the files within the Silverlight project. (This is achieved by selecting “Add existing item …”, then selecting the “Add as link” option.) The following diagram highlights the linked files in the attached project:

The first obstacle to overcome is the entry point into the application. Silverlight applications render a single UserControl as the RootVisual of the instantiated application, whereas WPF applications will create a Window instance when launched. This problem is easily solved by simply hosting the UserControl, which is the starting point for the Silverlight application, within a Window:

XML
<Window x:Class="ModalDialogDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ModalDialogDemo"
        Title="Modal dialog demo" SizeToContent="WidthAndHeight">
    <Grid>
        <local:MainControl/>
    </Grid>
</Window>

With this small obstacle cleared, you can create some pretty decent cross-framework applications (as long as you avoid the temptation of looking at all the WPF controls you are missing out on!).

I have created quite a few WPF / Silverlight applications in this manner, and the process, although a little clunky (I usually have two Visual Studio instances open so that I can keep the two versions in synch), works pretty well. However, the next major obstacle I experienced is that of creating a modal dialog. This falls firmly into category (3).

Modal dialog functionality was added in Silverlight 3. To pop-up a modal dialog, create a page which inherits from ChildWindow (rather than the usual UserControl). This class provides a Show method which pops up the dialog, and a Closed event which is raised when the user hits the OK or Cancel. For example, you might create an error dialog and use it as follows:

C#
ErrorDialog dlg = new ErrorDialog ();
dlg.Closed += new EventHandler(OnErrorDialogClosed);
dlg.Show();

Visual Studio has a template for the creating of modal dialogs via ChildWindow:

However, with WPF, things are a bit different. Whilst Silverlight modal dialogs are constrained to live within the Silverlight container on your web page, WPF dialogs are proper windows that you can move around your desktop (Interestingly, someone on stackoverflow has emulated Silverlight’s ChildWindow in WPF, although I am not convinced that this will look right on a desktop application). Visual Studio does not have a template for creating modal dialogs in WPF, however, there is more than enough documentation regarding the various types of dialog on MSDN.

With WPF, a Window can be shown as modal as follows:

C#
ErrorDialog dlg = new ErrorDialog ();
dlg.ShowDialog();

There are a few differences here, firstly Window has a Show method just like Silverlight’s ChildWindow, however, this will result in showing a modeless window. Instead we invoke the ShowDialog method which shows a modal dialog, but there is another subtle difference, whilst ChildWindow.Show returns immediately, with an event handler required to capture the modal dialog being closed, WPF’s Window.ShowDialog blocks until the dialog is closed.

So … each framework has different classes for modal dialogs, and different interfaces for each. How do we resolve these differences?

Early in this blog post, we saw how the UserControl which is the entry point into our Silverlight application can be hosted in a WPF Window. The same approach can be used for modal dialogs. We can create our modal dialog as a UserControl:

XML
<UserControl x:Class="ModalDialogDemo.ModalDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">    
    <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
 
        <TextBlock Text="What is your favourite colour? " VerticalAlignment="Center"/>
        <TextBox x:Name="colourInput" Grid.Column="1" 
		VerticalAlignment="Center" Width="80"/>
    </Grid>
</UserControl>

With the above control hosted in a ChildWindow or Window depending on the platform, the problem is, we cannot explicitly make a reference to either of these types in our common code (i.e. the code shared by WPF / Silverlight via file linking). This problem can be solved by employing the Gang of Four Adapter pattern.

We define an interface that gives the functionality that we require for hosting our UserControl as a modal dialog:

C#
/// <summary>
/// An interface which provides a host for some content which should be rendered
/// as a modal dialog
/// </summary>
public interface IModalDialogHost
{
    /// <summary>
    /// Gets or sets the content to host
    /// </summary>
    UserControl HostedContent { get; set; }
 
    /// <summary>
    /// Gets the dialog title
    /// </summary>
    string Title { set; }
 
    /// <summary>
    /// Gets the dialog result (i.e. OK, Cancel)
    /// </summary>
    bool? DialogResult { get; }
 
    /// <summary>
    /// Shows the dialog, invoking the given callback when it is closed
    /// </summary>
    void Show(DialogClosed closedCallback);        
}
 
/// <summary>
/// A callback which is invoked when a modal dialog is closed.
/// </summary>
public delegate void DialogClosed(IModalDialogHost host);

Within the Silverlight project, we create a concrete implementation of this interface based on ChildWindow:

XML
<controls:ChildWindow x:Class="ModalDialogDemo.ModalDialogHost"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:local="clr-namespace:ModalDialogDemo"
           xmlns:controls="clr-namespace:System.Windows.Controls;
			assembly=System.Windows.Controls">
    <Grid x:Name="LayoutRoot" Margin="2">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
 
        <!-- the hosted content -->
        <ContentPresenter x:Name="contentHost"/>
 
        <Button x:Name="CancelButton" Content="Cancel" 
		Click="CancelButton_Click" Width="75"
                Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />
        <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" 
		Width="75" Height="23"
                HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" />
    </Grid>
</controls:ChildWindow>

With the interface implemented in code-behind as follows:

C#
/// <summary>
/// A Silverlight implementation of IModalDialogHost
/// </summary>
public partial class ModalDialogHost : ChildWindow, IModalDialogHost
{
    ...
 
    public UserControl HostedContent
    {
        get
        {
            return (UserControl)contentHost.Content;
        }
        set
        {
            contentHost.Content = value;
        }
    }
 
    public void Show(DialogClosed closedCallback)
    {
        Show();
 
        // handle the Closed event, invoking the callback provided by the client
        Closed += (s, e) =>
            {
                closedCallback(this);
            };
    }
 
    public new string Title
    {
        set { base.Title = value; }
    }
}

Within WPF, we have a similar implementation based on Window:

XML
<Window x:Class="ModalDialogDemo.ModalDialogHost"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        SizeToContent="WidthAndHeight"
        ShowInTaskbar="False">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
 
        <!-- the hosted content -->
        <ContentPresenter x:Name="contentHost" Margin="10"/>
 
        <Button x:Name="CancelButton" Content="Cancel" 
		Click="CancelButton_Click" Width="75"
                Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />
        <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75"
                Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" />
    </Grid>
</Window>

Note that with WPF, we have a few extra configuration options to consider, such as the ShowInTaskbar property. The code-behind again implements our adapter interface:

C#
/// <summary>
/// A WPF implementation of IModalDialogHost
/// </summary>
public partial class ModalDialogHost : Window, IModalDialogHost
{
    ...
 
    public UserControl HostedContent
    {
        get
        {
            return (UserControl)contentHost.Content;
        }
        set
        {
            contentHost.Content = value;
        }
    }
 
    public void Show(DialogClosed closedCallback)
    {
        ShowDialog();
 
        // invoke the callback when the dialog is closed
        closedCallback(this);
    }
}

So, within each project, we have a framework specific implementation of this host. This allows us to create modal dialogs from the common / core code as follows:

C#
IModalDialogHost dlg = new ModalDialogHost();
dlg.Title = "Pick a Colour";
dlg.HostedContent = new ModalDialog();
dlg.Show(DialogClosedHandler);
 
...
 
private void DialogClosedHandler(IModalDialogHost dialogHost)
{
    string colour = ((ModalDialog)dialogHost.HostedContent).SelectedColour;
    resultTextBox.Text = colour;                        
}

Here is a Silverlight application with a dialog implemented using this hosting concept:

[Unfortunately CodeProject does not support Silverlight content - you can see this application in action on my blog.]

And here is a screenshot of the equivalent WPF application, where you can see the modal dialog is popped up as a window:

(If you want to have a go with the WPF app, you can download the source code from the link at the top of this blog.)

One final note … I have deliberately steered clear of the whole patterns business in this blog post. As you are probably aware, the Model-View-ViewModel (MVVM) pattern is very popular within Silverlight and WPF, and often other patterns such as Service Locator and Dependency Injection are thrown into the mix. However, the community has been struggling a little with how best to model modal dialog behaviours from within an MVVM ViewModel. Interestingly a couple of developers have supplied solutions on CodeProject and personal blogs, both faced questions and even criticisms that their approaches were not correct and unit testable (one of the central tenets of MVVM) and quickly followed up with articles demonstrating that you could in fact unit test their code (published here and here).

Whilst the use of a pattern such as MVVM which enforces a strong separation of application logic from the view will certainly help to solve problems such as WPF / Silverlight framework differences, in this particular instance, I wanted to show a simple technical solution rather than walk into the awaiting patterns minefield!

Regards, Colin E.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Scott Logic
United Kingdom United Kingdom
I am CTO at ShinobiControls, a team of iOS developers who are carefully crafting iOS charts, grids and controls for making your applications awesome.

I am a Technical Architect for Visiblox which have developed the world's fastest WPF / Silverlight and WP7 charts.

I am also a Technical Evangelist at Scott Logic, a provider of bespoke financial software and consultancy for the retail and investment banking, stockbroking, asset management and hedge fund communities.

Visit my blog - Colin Eberhardt's Adventures in .NET.

Follow me on Twitter - @ColinEberhardt

-

Comments and Discussions

 
-- There are no messages in this forum --