Saturday, January 17, 2009

Building a Modal Popup Framework with Silverlight

This post will demonstrate one way to create a modular and flexible framework for endowing your Controls with the ability to pop up in modal and non-modal modes.

Goals:
  1. Allow any Control to be "popped up" in modal and non-modal modes
  2. Construct a framework that is modular and made up of discrete, reusable components
  3. Properly hide implementation details and expose a simple api for developers to utilize.
  4. Reduce the amount of customization to the framework required by front-end developers.
Deconstructing the modal framework:
When modeling the framework, I thought it appropriate to use a framed painting as a metaphor.
  • A frame can enclose any painting. The frame itself can be reused.
  • A painting can be any painting. As long as it's a "painting" we can frame it.
  • A framed painting can be hung on a wall.
And so, the modal framework should be modeled similarly:
  1. We want a generic "frame" control that provides possibilities to adorn the frame with elements as well as the ability for the frame control to enclose any other silverlight control.
  2. The "painting" is any silverlight element that subclasses FrameworkElement. This covers (all?) user and custom controls.
  3. A Dialog factory is responsible for assembling the frame, the painting, and most importantly for providing the modal or non-modal popup behavior for the assembled "painting".
The code along with a demo exercising the popups is available for download from: http://yuriy.org/downloads/org.yuriy.Silverlight.DialogFramework.zip. It is released under LGPL and I hope it is useful. The code is decently well documented and I encourage you to explore the implementation. Of course, please contact me with any errata or feedback.

Implementation Design

AbstractFrame - is a custom control comprised of a c# class and associated templating in Themes/generic.xaml. It is the proverbial "frame" as described in pt 1 above. Its two responsibilities are:
  1. Establish default layout for Header, Body, Footer place holders - ContentPresenters exposed as custom DependencyProperties.
  2. Establish a default theme for our dialog popup.
Imagine that our corporate the branding team already decided on a standard look'and'feel for all dialog popups. We style our generic frame here so that all components utilizing this one get our desired default style for free.

Additionally, the exposed ContentPresenters grant the developer the "right" amount of flexibility. Let's refer to this as "Guided Development".

FrameImpl - FrameImpl is not a true "implementation" of an abstract class because AbstractFrame is not an "abstract" class. In fact, both subclass ContentControl. In a way, Abstraction and Implementation are be modeled in XAML through the use of Dependency Properties and what I'll refer to as Wrapping. If you take a look at generic.xaml, you will notice that the template for FrameImpl is nothing more than an AbstractFrame declaration with the Header and Body ContentPresenters further defined. This wrapping behavior is, in a sense, akin to subclassing in OO.

Through quite a few hours of exploration, I can state that Controls development in Silverlight favors Composition over Inheritance. Perhaps FrameImpl should have simply assembled three or four individual Controls rather than declare the details of Header, Body and Footer as part of the wrapper. In any case, to maintain my initial metaphor, I favored Inheritance.

As stated earlier, the inheritance happens in XAML, NOT within the code-behind classes. Both FrameImpl and AbstractFrame subclass ContentControl so that Silverlight can guarantee correct binding to XAML.

Buttons and event handlers in FrameImpl
One of the important details in FrameImpl's is the declaration of Buttons, a single mechanism for handling button clicks, and a chaining mechanism that notifies the caller of a button click. Control is returned to the caller with an enum click status and a reference to the included control for easy post-processing that is decoupled from the implementation of the buttons itself.

Again, the layout, styling and available customizations are configured for desired default behavior and the available interface to the developer is reduced to simplify possible and appropriate customizations.

The buttons in this demo are specified as a Close Button in the upper right corner, and a ribbon of optional and configurable "left", "center", "right" buttons that can be used to model most common dialogs. The developer has the option to hide any or all of the buttons. Again, all the buttons are prewired. The developer just needs to specify an event handler when working within this framework.

DialogFactory
The final stage of my implementation is a Factory that:
  1. Exposes static Show() initializers to the developer. These are overloaded for button config flexibility, accept any Silverlight control as content for the popup, and an event handler to receive notifications of buttons clicks within the Dialog. These are contained in the DialogFactory.static partial class.
  2. The DialogFactory.effects partial class contains methods that contain point math for dragging of the dialog across the screen as well as resizing and centering the Dialog. Although not currently implemented, this partial class can also contain entrance and exit animation effects.
  3. The DialogFactory partial class contains the internal constructor, and processors for configuring buttons, handlig events, configuring modality and so on. DialogFactory appropriately delegates control to FrameImpl and binds app developer's handlers to events generated by FrameImpl.
DialogFactory has no associated XAML. It is purely a C# factory that uses the Popup primitive alongside a masking Canvas and a draggable Border to provide the modal popup behavior for any passed in Control. The Control is, of course, wrapped in a FrameImpl.

As a side-note, DialogFactory does account for a known Popup/Pulldown bug that causes Silverlight to crash.

Project Structure
The Solution is split into three Projects:
  1. Controls lib - the framework is contained in a standalone controls dll. It needs to be referenced in your projects. If you are implementing controls for your company, odds are they are in a separate library so that multiple apps can use them. The idea is the same here, even though this demo only has one control!
  2. A Silverlight App - this contains a DialogDemo UserControl that exercises the DialogFramework.Show() istantiators and some sample Controls to be passed into the initializer.
  3. ASP.NET project - this is the project that loads (2).
Run the Demo
  • Download the code and open it in VS.
  • Make sure that the ASP.NET project is set as the executing default. A form that allows you to play with DialogFramework will come up with your browser.
The Code. The code!
Again, you can download the code from: http://yuriy.org/downloads/org.yuriy.Silverlight.DialogFramework.zip. It is released under LGPL. I believe it is decently well documented and provides insights into my implementation choices.

Acknolwedgements
This was my first deep dive into Silverlight. I read articles from many developers. The work I present here is a synthesis of great work from several authors, notably: