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;
}
}
}