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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="https://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();

        }

    }

}

 

 

 

 

DevDaveDialogApp.zip