search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Silverlight Modal Dialog With Custom User Controls


By Robbe Morris
Printer Friendly Version
View My Articles
104 Views
    

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

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.

button
Article Discussion: Silverlight Modal Dialog With Custom User Controls
Robbe Morris posted at Saturday, July 18, 2009 2:20 PM
Original Article
 

Great article
This messageboard post is valuable. Matt Weyland provided a helpful reply to to Robbe Morris at Tuesday, July 21, 2009 12:10 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 Tuesday, July 21, 2009 7:42 PM
end of post