Introduction
Knowm Ercy's article Modal Window in Silverlight provided a nice way to display a Silverlight UserControl
in a way that looks and feels modal. Here at ProModel, Inc. we were interested in this functionality (which is not natively available within Silverlight at the moment) and its use in our Silverlight based applications. Using Knowm's article as a starting point, we put together an extension that "wraps" the hosted UserControl
inside a window frame which provides for resizing, moving, maximizing, and many of the other features enjoyed with traditional modal Windows forms.
This code is presented "as is" and we are aware of a number of improvements that can and should be made. However, given our in-house time constraints and the lack of a similar modal solution out there (a free one anyway), we decided to make this source available for improvement by anyone who's up for the challenge.
Background
Although we've made some departures from the original architecture, it is basically as we found it, just extended, so reading Knowm Ercy's article Modal Window in Silverlight should prove helpful in understanding the code.
There are two classes that work to provide the basic modal display:
Modal
A simple class containing the popup control which host the "modal" form and provides basic show/hide functionality.
ModalHost
A class that hosts the UserControl
which is to be displayed modally. This class renders a "background canvas" that covers the entire browser window and prevents interaction with anything other than the hosted control, giving the impression that the hosted control is modal. Also, the ModalHost
class provides basic mouse drag functionality such that the hosted control can be moved if a mouse drag occurs on its surface.
Each of the two modal display classes contained in our library (ModalControl
, WindowedModalControl
) contain an instance of each of these class. In the case of ModalControl
, the simple base classes are used (as with Knowm's provided project). In the case of WindowedModalControl
, each class is derived to provide the extended functionality.
Here is a summary of the functionality provided in the derived classes:
WindowedModal
(derived from Modal
)
All that is done in this child class is a simple override of a method that sets the ModalHost
instance within the Modal
class. This allows us to insert our custom ModalHost
(WindowedModalHost
).
internal class WindowedModal : Modal
{
#region Method Declarations
protected override void SetHost(IModal ctrl, bool useCanvasPainting)
{
_host = new WindowedModalHost(ctrl, useCanvasPainting);
}
#endregion
}
WindowsModalHost
(derived from ModalHost
)
This class overrides a few basic ModalHost
behaviors/adds some simple functionality:
- Provides
RestoreState
property which keeps track of whether or not the WindowedModalControl
is maximized.
- Overrides the
CenterWindow()
method so that it takes the RestoreState
into account.
- Overrides mouse events to prevent the base class from taking any action.
public override void CentreWindow()
{
double dblX = 0.0, dblY = 0.0;
UserControl modal = _modal as UserControl;
if (RestoreState == RestoreState.Unmaximized)
{
if (_modal != null && !double.IsNaN
(WindowHelpers.GetControlSize(modal).Width) &&
!double.IsNaN(Application.Current.Host.Content.ActualWidth))
{
dblX = ((Application.Current.Host.Content.ActualWidth -
WindowHelpers.GetControlSize(modal).Width) / 2);
modal.SetValue(Canvas.LeftProperty, dblX);
}
if (_modal != null && !double.IsNaN
(WindowHelpers.GetControlSize(modal).Height) &&
!double.IsNaN(Application.Current.Host.Content.ActualHeight))
{
dblY = ((Application.Current.Host.Content.ActualHeight -
WindowHelpers.GetControlSize(modal).Height) / 2);
modal.SetValue(Canvas.TopProperty, dblY);
}
}
protected override void OnMouseLeftButtonDown
(object sender, MouseButtonEventArgs e)
{
}
protected override void OnMouseMove(object sender, MouseEventArgs e)
{
}
The WindowedModalControl
class provides the major functional extensions to Known's original work. This includes:
- Managing the placement of the hosted
UserControl
inside of a window frame
- Support for Maximize/Restore
- Support for display of window as both resizing and non-resizing
- A title bar with image and auto-truncating text caption
- Built-in OK, Cancel, Apply button support
- Auto-sizing of the hosted
UserControl
- Usage of custom brushes for form header, background and text
Using the Code
Simply hosting your UserControl
within the WindowedModalControl
will look like this:
public void ShowWindowedModal(DialogClose callback, Panel panel)
{
bool useCanvasPainting = true;
WindowedModalControl wmc = new WindowedModalControl
(callback, MsgBoxButtons.OKCancelApply);
TestUserControl tc = new TestUserControl();
wmc.HostedControl = tc;
wmc.ShowModal(panel, useCanvasPainting);
}
This sample makes use of some of the more advanced features:
public void ShowWindowedModal(DialogClose callback, Panel panel)
{
bool useCanvasPainting = true;
WindowedModalControl wmc = new WindowedModalControl
(callback, MsgBoxButtons.OKCancelApply);
TestUserControl tc = new TestUserControl();
wmc.TitleBarBrush = Resources["ModalHeaderBrush1"] as Brush;
wmc.FormBackgroundBrush = Resources["ModalBrush1"] as Brush;
wmc.TitleImage = _titleImage;
wmc.HostedControl = tc;
wmc.ShowModal(panel, useCanvasPainting);
wmc.AllowResizing = true;
wmc.AllowMaximize = true;
wmc.TitleBarText = "It's a Working Title...and it truncates if need be";
}
The provided source comes with a demo application that has source code samples alongside run-time examples. A compiled help file is also included.
Special Thanks
To Knowm Ercy for the work he put into his article.