Silverlight Modal Dialog With Custom User Controls

The Silverlight code sample below shows one way to launch custom user controls in a ModalDialog without the standard windows border or X to close the window. It also automatically centers the ModalDialog.

The basic jist of this technique is to have a static class act as a hook to launch an automatically centered PopUp control along with any of your custom controls dropped on it.  What makes it modal is that the ModalDialog.xaml user control uses a transparent background that takes up the entire screen space and displays your custom control right in the middle of the page.  So, while the user can still see the main application, the user cannot interact with it.  You could take this even further and incorporate a really nice effect such as a white or grey glass effect as the ModalDialog background.  This notifies the user that the dialog needs to be responded to first prior to returning to the application.

As a general rule, you could launch the dialog with one line of code, set up your custom callback event when the dialog closes, and trigger the close from within your custom control with one line of code.  Notice that the ModalDialogEventArgs holds a simple int property DialogResult.  While it does not enforce a valid enum, it does give your application a way to reuse this process for many different types of user controls and many different enum oriented dialog result codes.  The enforcement of the enum occurs in your application and not in this reusable ModalDialog library.

The only drawback to this simplistic approach is that it doesn't support launching modal dialogs from a modal dialog.  For those specific instances, you'll want to wire up a separate Popup control and manage it accordingly.  In my applications, I put the ModalDialogController, ModalDialogEventArgs, ModalDialog.xaml, and ModalDialog.xaml.cs files in a reusable assembly across various Silverlight and WPF applications.

The downloadable source code uses a Silverlight 3.0 project file.  However, the code will work in Silverlight 2.0.  You just need to use a Silverlight 2.0 project file instead.

  Download Source Code

Here is some of the relevant code:

// App.xaml.cs

public App()
{
  this.Startup += this.Application_Startup;
  this.Exit += this.Application_Exit;
  this.UnhandledException += this.Application_UnhandledException;
  // Wire up the content resize event to enable us to automatically center the modal dialog box.
  this.Host.Content.Resized += new EventHandler(ModalDialogController.ApplicationContent_Resized);
  InitializeComponent();
}

// MainPage.xaml

<UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <StackPanel Orientation="Horizontal" Height="30" VerticalAlignment="Top" HorizontalAlignment="Center">
        <Button x:Name="UserControl1Sample" Click="UserControl1_Click" Content="Launch User Control 1"/>
        <Button x:Name="UserControl2Sample" Click="UserControl2_Click" Content="Launch User Control 2"/>
        <Button x:Name="UserControl3Sample" Click="UserControl3_Click" Content="Launch User Control 3"/>
    </StackPanel>
</UserControl>

// MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace SilverlightApplication2
{
    public partial class MainPage : UserControl
    {
         public MainPage()
        {
             InitializeComponent();
        }

         private void UserControl1_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Launch<UserControl1>(new UserControl1(),
                                                       OnModalDialogUserControl1_Closed);  
         }
        private void UserControl2_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Launch<UserControl2>(new UserControl2(), OnModalDialogUserControl2_Closed);  
         }
         private void UserControl3_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Launch<UserControl3>(new UserControl3(), OnModalDialogUserControl3_Closed);  
         }

         private void OnModalDialogUserControl1_Closed(object sender, ModalDialogEventArgs e)
        {
            var control = sender as UserControl;
            MessageBox.Show(control.Name + " dialog result is " + e.DialogResult.ToString());
         }
        private void OnModalDialogUserControl2_Closed(object sender, ModalDialogEventArgs e)
        {
            var control = sender as UserControl;
            MessageBox.Show(control.Name + " dialog result is " + e.DialogResult.ToString());
         }
        private void OnModalDialogUserControl3_Closed(object sender, ModalDialogEventArgs e)
        {
            var control = sender as UserControl;
            MessageBox.Show(control.Name + " dialog result is " + e.DialogResult.ToString());
         }

    }
}

// UserControl1.xaml

<UserControl x:Class="SilverlightApplication2.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300" Name="UserControl1PlaceHolder">
    <Border Style="{StaticResource ModalDialogBorder}" >
        <Grid x:Name="LayoutRoot" Margin="10,10,10,10">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBox Text="Text Box 1" TabIndex="0" Grid.Row="0"/>
            <TextBox Text="Text Box 2" TabIndex="1" Grid.Row="1"/>
            <TextBox Text="Text Box 3" TabIndex="2" Grid.Row="2"/>
            <TextBox Text="Text Box 4" TabIndex="3" Grid.Row="3"/>

            <StackPanel Orientation="Horizontal" Grid.Row="4">
                <Button x:Name="SubmitButton" Click="SubmitButton_Click" Content="Submit" TabIndex="4"/>
                <Button x:Name="RegisterButton" Click="RegisterButton_Click" Content="Register" TabIndex="5"/>
                <Button x:Name="ForgotPasswordButton" Click="ForgotPasswordButton_Click" Content="Forgot Password"

TabIndex="6"/>
                <Button x:Name="CancelButton" Click="CancelButton_Click" Content="Cancel" TabIndex="7"/>
            </StackPanel>
        </Grid>
     </Border>
</UserControl>

// UserControl1.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace SilverlightApplication2
{
    public partial class UserControl1 : UserControl
    {
         public UserControl1()
        {
             InitializeComponent();
        }

         private void SubmitButton_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Close<UserControl1>(this, new ModalDialogEventArgs((int)

Enums.LoginDialogResultTypes.SignInSuccessful));  
        }
         private void RegisterButton_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Close<UserControl1>(this, new ModalDialogEventArgs((int)

Enums.LoginDialogResultTypes.CreateNewAccount));  
        }
         private void ForgotPasswordButton_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Close<UserControl1>(this, new ModalDialogEventArgs((int)

Enums.LoginDialogResultTypes.ForgotPassword));  
        }
         private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            ModalDialogController.Close<UserControl1>(this, new ModalDialogEventArgs((int)

Enums.LoginDialogResultTypes.Cancel));  
         }
    }
}

// ModalDialog.xaml

<UserControl x:Class="SilverlightApplication2.ModalDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid x:Name="LayoutRoot" Background="Transparent"  >
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.ColumnSpan="3" />
        <Rectangle Grid.Row="1" Grid.Column="0"    />
        <Grid Grid.Row="1" Grid.Column="1" x:Name="UserControlPlaceHolder" HorizontalAlignment="Center"

VerticalAlignment="Center" />
        <Rectangle Grid.Row="1" Grid.Column="2" />
        <Rectangle Grid.Row="2" Grid.ColumnSpan="3" />
    </Grid>
</UserControl>



// ModalDialog.xaml.cs

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace SilverlightApplication2
{
    public partial class ModalDialog : UserControl
    {
         public ModalDialog()
        {
             InitializeComponent();
            this.TabNavigation = KeyboardNavigationMode.Cycle;
         }
        public void AddControl<T>(T userControl) where T : UserControl
        {
               this.UserControlPlaceHolder.Children.Add(userControl);
         }
    }
}


// ModalDialogController.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Controls.Primitives;

namespace SilverlightApplication2
{
    
     public static class ModalDialogController  
    {

        private static Popup _popUp = new Popup();
        private static ModalDialog _dialog = null;
        private static event EventHandler<ModalDialogEventArgs> RequestDialogClose;
         private static double _applicationHeight= 0;
        private static double _applicationWidth = 0;

        public static void Launch<T>(T modalDialogUserControl,
                                                              EventHandler<ModalDialogEventArgs> eventHandler) where

T: UserControl
        {
            
            RequestDialogClose = eventHandler;

            _dialog = new ModalDialog();
            _dialog.AddControl<T>(modalDialogUserControl);
            _dialog.Height = _applicationHeight;
            _dialog.Width = _applicationWidth;

            _popUp.Child = _dialog;
            _popUp.IsOpen = true;
        }

         public static void Close<T>(T modalDialogUserControl,ModalDialogEventArgs eventArgs) where T: UserControl
        {
            _popUp.IsOpen = false;
            _dialog = null;
            _popUp.Child = null;
            if (RequestDialogClose == null) return;
            RequestDialogClose(modalDialogUserControl, eventArgs);
        }

        public static void ApplicationContent_Resized(object sender, EventArgs e)
        {
            var control = sender as UserControl;
             if (control == null) return;
            _applicationHeight = control.ActualHeight;
            _applicationWidth = control.ActualWidth;
             if (_dialog == null) return;
            _dialog.Height = _applicationHeight;
            _dialog.Width = _applicationWidth;
        }

    }  
}

// ModalDialogEventArgs.cs

using System;

namespace SilverlightApplication2
{
    public class ModalDialogEventArgs : System.EventArgs
    {
         public int DialogResult = -1;

        public ModalDialogEventArgs(int dialogResult)
        {
            DialogResult = dialogResult;
        }
    }
}
By Robbe Morris   Popularity  (11941 Views)
Picture
Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.  Robbe also loves to scuba dive and go deep sea fishing in the Florida Keys or off the coast of Daytona Beach. Microsoft MVP