Silverlight Modal Dialog With Custom User Controls

By Robbe Morris

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;
        }
    }
}
Popularity  (9376 Views)
Picture
Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of EggHeadCafe.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
Create New Account
Article Discussion: Silverlight Modal Dialog With Custom User Controls
Robbe Morris posted at Saturday, July 18, 2009 2:20 PM
Great article
Matt Weyland replied to Robbe Morris at Wednesday, October 20, 2010 10:32 AM
Thanks for the article, I took what you've done, and this evening worked it to suit my needs, specifically around storyboards and centering horizontally and vertically for a nested control.  In my instance the control that was calling the modaldialog was 7 layers deep on the application.

With the code you provided my popup window was not centering vertically or horizontally, and was also not representing any height or width.  With a couple of code changes to the Lauch method I worked around this.

I also added some storyboards to the ModalDialog.xaml file and am calling these on the Launch and Close methods with an extra method to handling the sb completed event so the popup wasnt removed prior to the completion of the story board. 

Keep up the interesting articles. Hope to see more.

I uploaded the solution with the changes. I think this has a greater aesthetic feel.


Very cool, can you post the url to what you uploaded?
Robbe Morris replied to Matt Weyland at Wednesday, October 20, 2010 10:32 AM
end of post
Great work
Stephen Drew replied to Robbe Morris at Wednesday, October 20, 2010 10:32 AM
Very nice article, thanks a lot.
NSB replied to Stephen Drew at Wednesday, October 20, 2010 10:32 AM
Hi
it is very good articale. but i would like know how to pass values to popwind and return from popup wind, if you provide this it could be greate.

Regards
Suresh
Daniel replied to Robbe Morris at Wednesday, October 20, 2010 10:32 AM
Great Article indeed. I had some issues with the dialog being too big when the web browser window was too small. I had a ScrollView in my dialog but it didn't have any effect because the size of the custom dialog was not resized properly.

I fixed this by adding the following lines in ApplicationContent_Resized:

        _dialog.UserControlPlaceHolder.MaxHeight = _applicationHeight;
        _dialog.UserControlPlaceHolder.MaxWidth = _applicationWidth;