Welcome to MSDN Blogs Sign in | Join | Help

Using Popup to create a Dialog class

Presenting a dialog to the user is a common task for many applications. Silverlight 2 does not have a Dialog class, but it is possible to use the Popup class to create a very credible dialog. My Dialog class can't leave the Silverlight plugin--for that you would have to use Javascript interop to create a new Silverlight plugin, etc. and that's a topic for somebody else's blog :)

The key here is to make the Popup full screen (and resize it when the plugin size changes) and also have a Canvas that is full screen. Then, if you want to have a modal dialog, set the Canvas's Background property. This will prevent mouse interaction with the rest of your application. This will also enable you to detect when the user clicks outside of your dialog content, in case you want to dismiss the dialog that way.  

The one caveat that I can think of is that keyboard events can still follow the focus, which can be set to elements beneath the modal dialog. I have some ideas how to fix this in the RTM timeframe.

To use the Dialog class, you will need to subclass it, and provide an override for the GetContent method. The content that you provide will become centered (in a Grid) on the screen.

Here is the code for the Dialog class:

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Controls.Primitives;

using System.Windows.Media;

using System.Windows.Shapes;

 

namespace DialogApp

{

    public abstract class Dialog

    {

        public void Show(DialogStyle style)

        {

            if (_isShowing)

                throw new InvalidOperationException();

 

            _isShowing = true;

 

            EnsurePopup(style);

 

            _popup.IsOpen = true;

 

            Application.Current.Host.Content.Resized += OnPluginSizeChanged;

        }

 

        public void Close()

        {

            _isShowing = false;

 

            if (_popup != null)

            {

                _popup.IsOpen = false;

                Application.Current.Host.Content.Resized -= OnPluginSizeChanged;

            }

        }

 

        // Override this method to add your content to the dialog

        protected abstract FrameworkElement GetContent();

 

        // Override this method if you want to do something (e.g. call Close) when you click

        // outside of the content

        protected virtual void OnClickOutside() { }

 

        // A Grid is the child of the Popup. If it is modal, it will contain a Canvas, which

        // will be sized to fill the plugin and prevent mouse interaction with the elements

        // outside of the popup. (Keyboard interaction is still possible, but hopefully when

        // Silverlight 2 RTMs, you can disable the root to take care of that.) The Grid isn't

        // strictly needed if there is always a Canvas, but it is handy for centering the content.

        //

        // The other child of the Grid is the content of the popup. This is obtained from the

        // GetContent method.

 

        private void EnsurePopup(DialogStyle style)

        {

            if (_popup != null)

                return;

 

            _popup = new Popup();

            _grid = new Grid();

            _popup.Child = _grid;

 

            if (style != DialogStyle.NonModal)

            {

                // If Canvas.Background != null, you cannot click through it

 

                _canvas = new Canvas();

                _canvas.MouseLeftButtonDown += (sender, args) => { OnClickOutside(); };

 

                if (style == DialogStyle.Modal)

                {

                    _canvas.Background = new SolidColorBrush(Colors.Transparent);

                }

                else if (style == DialogStyle.ModalDimmed)

                {

                    _canvas.Background = new SolidColorBrush(Color.FromArgb(0x20, 0x80, 0x80, 0x80));

                }

 

                _grid.Children.Add(_canvas);

            }

 

            _grid.Children.Add(_content = GetContent());

 

            UpdateSize();

        }

 

        private void OnPluginSizeChanged(object sender, EventArgs e)

        {

            UpdateSize();

        }

 

        private void UpdateSize()

        {

            _grid.Width  = Application.Current.Host.Content.ActualWidth;

            _grid.Height = Application.Current.Host.Content.ActualHeight;

 

            if (_canvas != null)

            {

                _canvas.Width = _grid.Width;

                _canvas.Height = _grid.Height;

            }

        }

 

        private bool _isShowing;

        private Popup _popup;

        private Grid _grid;

        private Canvas _canvas;

        private FrameworkElement _content;

    }

 

    public enum DialogStyle

    {

        NonModal,

        Modal,

        ModalDimmed

    };

}

 

Here is how I use it in my sample:

<UserControl x:Class="DialogApp.Page"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespace:DialogApp;assembly=DialogApp">

    <Border x:Name="LayoutRoot" Background="White">

        <StackPanel Margin="8" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left">

            <Button Height="24" Margin="2" Content="Show Popup" Click="Button_Click"/>

            <RadioButton Height="24" Margin="2" Content="Non-Modal" IsChecked="true"/>

            <RadioButton Height="24" Margin="2" x:Name="isModal" Content="Modal"/>

            <RadioButton Height="24" Margin="2" x:Name="isModalDimmed" Content="Modal Dimmed"/>

        </StackPanel>

    </Border>

</UserControl>

 

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Media;

 

namespace DialogApp

{

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            Dialog dlg = new MyDialog();

            if (isModal.IsChecked.Value)

                dlg.Show(DialogStyle.Modal);

            else if (isModalDimmed.IsChecked.Value)

                dlg.Show(DialogStyle.ModalDimmed);

            else

                dlg.Show(DialogStyle.NonModal);

        }

    }

 

    public class MyDialog : Dialog

    {

        protected override FrameworkElement GetContent()

        {

            // You could just use XamlReader to do everything except the event hookup.

 

            Grid grid = new Grid() { Width = 200, Height = 200, };

            Border border = new Border() { BorderBrush = new SolidColorBrush(Colors.Black), BorderThickness = new Thickness(2), CornerRadius = new CornerRadius(4), Background = new SolidColorBrush(Colors.White) };

            grid.Children.Add(border);

            grid.Children.Add(new TextBlock() { Text = "Dialog", HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, Margin = new Thickness(8) });

            Button button = new Button() { Width = 50, Height = 24, Content = "Close", HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Bottom, Margin = new Thickness(8) };

            grid.Children.Add(button);

            button.Click += (sender, args) => { Close(); };

 

            return grid;

        }

 

        protected override void OnClickOutside()

        {

            //Close();

        }

    }

}

 

 

 

 

Published Sunday, June 08, 2008 3:59 AM by Dave Relyea
Filed under: , ,

Attachment(s): DevDaveDialogApp.zip

Comments

# re: Using Popup to create a Dialog class

Sunday, June 08, 2008 7:59 PM by Mark Rideout [msft]

You should set TabNavigation=Cycle for your user control so the user doesn't tab outside it on the modal cases.

-mark

# re: Using Popup to create a Dialog class

Wednesday, October 01, 2008 12:36 PM by schmiddy

What if you need to design your dialogs in xaml rather than coding everything up in C# in the GetContent() method?

This may be important if you have lots of complex dialogs that designers create in Blend.

# re: Using Popup to create a Dialog class

Wednesday, October 01, 2008 12:40 PM by Dave Relyea

schmiddy - In that case, you can make the dialog subclass return a new UserControl subclass for its content--and design the UserControl subclass as normal.

# Crashes on RTW

Thursday, October 23, 2008 12:11 AM by maxchristian

This doesn't work on 2.0 RTW if the content is non-trivial, e.g. try adding a ListBox inside the dialog and clicking on it a few times -- you get "System Exception: Catastrophic failure".

I've found that doing LayoutRoot.Children.Add(_popup) sorts it, not quite sure of the implications of that though.

# re: Using Popup to create a Dialog class

Thursday, October 23, 2008 11:45 AM by Dave Relyea

maxchristian, yes, anything that calls TransformToVisual under the hood will crash unless you do what you suggest.

The implications are that the popup will be affected by the RenderTransforms that apply to the LayoutRoot, but if you are a little clever you can also get around that if need be.

# re: Using Popup to create a Dialog class

Friday, October 24, 2008 10:50 AM by maxchristian

Ah, so that's why my dialog became enormous when I made the SL control fill the browser window using a RenderTransform...

Can you be more specific?  I'm not sure if I'm a little clever but I'm certainly a little lazy.  ;)

# re: Using Popup to create a Dialog class

Friday, October 24, 2008 9:15 PM by Dave Relyea

maxchristian,

You may need to change where you put the RenderTransforms, and/or add an additional element. If you had this:

<UserControl>

   <Grid Name=LayoutRoot>

and the RenderTransforms were on the UserControl or the Grid, and you added the Popup to the Grid, then you may need to do something like this:

<UserControl>

   <Grid Name=newRoot>

       <Grid Name=LayoutRoot>

Add the newRoot Grid, put the RenderTransforms on the LayoutRoot, and add the Popup to the newRoot.

# Keyboard navigation, download link broken

Thursday, March 05, 2009 2:25 PM by kobruleht

How to allow to use tab and left/right arrow keys to navigate between buttons ?

Currently keyboard naviation is not available.

Also, zip file dowload link is broken.

Andrus.

# re: Using Popup to create a Dialog class

Monday, May 25, 2009 1:22 PM by tearaway_Tea

Thanks. This is a great class!

# re: Using Popup to create a Dialog class

Monday, June 22, 2009 7:56 PM by kpinckert

I am unable to put a ComboBox on a UserControl that is used for a popup window.  When I popup the control and touch the ComboBox it hangs.  It can be empty or not. Using SL2.  Please advise.  Thank you.

# re: Using Popup to create a Dialog class

Tuesday, June 23, 2009 2:04 PM by Dave Relyea

This is likely due to an SL2 bug. ComboBoxes in a Popup, when the Popup is not in the visual tree (i.e. in the page's XAML) don't work. This has been fixed for SL3.

# re: Using Popup to create a Dialog class

Wednesday, June 24, 2009 4:02 PM by kpinckert

Ok, thanks.  A work around then is to wrap your ComboBox in another UserControl in some way; it just can't sit directly on the popup control.  I tried that  and  it seems to be working.

Thanks for the help.

Anonymous comments are disabled
 
Page view tracker