Introduction
This article examines how to design and create dynamic user interfaces using the Windows Presentation Foundation (WPF). Along the way we will explore a demo application which uses XAML for creating a dynamic user interface to display rich and interactive alert messages to the user.
Background
The term "dynamic user interface" refers to a user interface which hosts arbitrary content discovered at runtime. Desktop applications traditionally consist of user interfaces which are designed and compiled up front, and then remain relatively static after the application is deployed. The static nature of a traditional desktop application imposes limitations on what type of content can be displayed. Many of those limitations have been more easily addressed with Web-based UI platforms, due to the fact that Web UIs are always downloaded and based on markup (namely, HTML).
With the advent of WPF it has become much easier to download and display portions of a user interface while the application is running. Not only can you easily display arbitrary UI content, but you can also associate runtime behavior with those visual elements. For example, it is fairly easy to create a WPF application which downloads a portion of its user interface and then reacts to, say, a dynamically loaded button being clicked by the user. This opens a wide range of new possibilities for desktop application developers.
Nothing is perfect
This article shows one way to create a dynamic user interface in WPF. There are certainly other approaches which you could use, such as downloading an assembly with a UserControl
in it and then using reflection to load an instance of that control into your UI. The approach outlined in this article is XAML-based, which makes it very flexible and allows you to expose portions of a user interface via XML Web services.
Naturally each solution to a particular problem has its pros and cons, so there is no absolute "best" way to depend on. For example, the approach shown in this article expects the application to know how to respond to user interaction with elements in the dynamic UI content. If a user clicks on a dynamically loaded Hyperlink
control, the application must respond accordingly. The approach involving a dynamically loaded UserControl
would not have that restriction since the UserControl
can contain its own interaction logic. However, since a UserControl
cannot be downloaded as XML, you might encounter firewall/security issues if you choose to download portions of a dynamic UI as a DLL.
The three pillars
There are three essential steps to creating a dynamic user interface, if you choose to use the approach described in this article. We will review those three pillars in this section, and then see them in use in the next section.
XAML
The UI content which is dynamically loaded must be serialized in XAML. In other words, your application must be able to access some XAML and turn it into live visual elements which can be displayed. The secret ingredient in this process is the XamlReader.Load method.
Content Container
An application which hosts dynamic UI content must have a place to put that content. After you have turned some XAML into visual elements you need to display them somewhere. You can use a ContentControl
or a ContentPresenter
to contain the visual elements (read about the differences between those two elements here).
Interaction Notifications
Your dynamic UI should not be a trophy wife. If it does not do something other than look good, you probably don't need it around. You have two options for associating behavior with dynamically loaded visual elements: routed events and routed commands.
Since routed events/commands can bubble up the element tree you can add basic interaction logic in your application, at compile-time. When dynamic content is loaded and the user interacts with it, your pre-built interaction logic can perform the necessary actions in response to user input.
For example, if your dynamic UI will usually contain a Hyperlink
in it, then your application should provide a means of responding to a Hyperlink
being clicked. As I mentioned previously, the fact that the application must have baked-in logic to handle user interaction is a shortcoming of using the XAML-based approach to creating dynamic UIs. However, that shortcoming might not be an issue for applications with relatively simple dynamic UI needs.
What the demo application does
This article is accompanied by a demo application which shows how to implement a dynamic UI. The demo application is an "imaginary" business app (i.e. it has no actual functionality) which displays a dynamic alert message to the user.
The idea behind this use of dynamic user interfaces is that an alert message can be placed on a server, and when the application is run, it retrieves and displays the message. The alert is stored as XML, which happens to contain valid XAML (remember, XAML is an XML-based language). The alert XML data could be retrieved by the application via an XML Web service, or from a database call, or from a network drive, etc.
This dynamic alert message functionality allows the application to be deployed with no knowledge of what the alert messages might contain, or when they will exist. When an alert message is placed on a server, the application can simply download it and display whatever content it happens to contain. Since the alert is declared in XAML it can make use of the entire WPF platform; such as including images, video, audio, or even controls that do something in response to user input.
What the demo application looks like
Here is a screenshot of the alert message which is dynamically loaded when the demo application is run:
Everything in that alert interface is dynamically loaded, except for the gray title bar and the "[Close]" link at the bottom. If you were to click on the "here" link, it would open a Web browser to a specific page. The alert message uses the flow document technology in WPF to provide a smooth reading experience with a bulleted list. It would certainly be non-trivial to emulate this functionality in a Windows Forms application.
How the demo application works
Now that we have a firm understanding of what dynamic user interfaces are and the fundamental steps required to implement them in WPF, let's take a look how the demo application works. There are five pieces in this puzzle.
Alert XML data
First we will take a look at how an alert is declared. An alert is saved as XML, which can be retrieved from a server at runtime. Here is the alert XML data used in the demo application:
="1.0" ="utf-8"
<Alert Title="Company News">
-->
<FlowDocumentScrollViewer
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
VerticalScrollBarVisibility='Auto'
>
<FlowDocument>
<Paragraph>
The Foo Daddy Company is proud to announce
that it has a new Vice President, Ronald McDonald.
Mr. McDonald joins us after a long tenure at the
McDonald's Corporation, serving as Chief Happiness
Officer for over 20 years. Ronald McDonald can
speak 31 different languages including:
</Paragraph>
<List>
<ListItem>
<Paragraph>Mandarin</Paragraph>
</ListItem>
<ListItem>
<Paragraph>Dutch</Paragraph>
</ListItem>
<ListItem>
<Paragraph>Tagalog</Paragraph>
</ListItem>
<ListItem>
<Paragraph>Hindi</Paragraph>
</ListItem>
</List>
<Paragraph>
Read more about Mr. McDonald's background
<Hyperlink
NavigateUri='http://en.wikipedia.org/wiki/Ronald_Mcdonald'
ToolTip="View Mr. McDonald's profile">
here
</Hyperlink>.
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</Alert>
There are two important aspects in the XML data seen above. The root <Alert>
element has a Title
attribute. The title is displayed above the alert content in the user interface. The inner XML of the <Alert>
element is the XAML which constitutes the alert's message. That XAML is dynamically loaded and rendered by the host application.
The Alert class
The title and content of an alert need to be stored somewhere. I chose to store that information in a simple class called Alert
. Here is the entire Alert
class:
public class Alert
{
readonly object content;
readonly string title;
public Alert( string title, object content )
{
if( content == null )
throw new ArgumentNullException( "content" );
this.title = String.IsNullOrEmpty(title) ? "Alert" : title;
this.content = content;
}
public object Content
{
get { return this.content; }
}
public string Title
{
get { return this.title; }
}
}
Main application Window
In this demo application the main Window
does not really do much. It has no real functionality. All that it does is check to see if an alert exists, and then displays it if one does exist. In a real application the main Window
would certainly have more to it than this, but let's keep it simple for the sake of this demo. Here's the code-behind for the main Window
:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += delegate
{
this.CheckForAvailableAlert();
};
}
void CheckForAvailableAlert()
{
Alert alert = AlertProvider.GetLatestAlert();
if( alert != null )
new AlertWindow( this, alert ).ShowDialog();
}
}
AlertProvider
The main Window
relies on a class called AlertProvider
to determine if there is an alert to display to the user. AlertProvider
is responsible for "checking" if there is an alert to display and, if there is, returning information about it. Keep in mind that in this demo the actual alert data is not being retrieved from an external source. The alert XML is a resource in the application assembly, but in a real application the alert would be retrieved from an external source. Here is the sole public member of AlertProvider
:
public static Alert GetLatestAlert()
{
Alert alert = null;
XmlTextReader xmlRdr = null;
try
{
xmlRdr = RetrieveAlertXml();
bool alertExists = xmlRdr != null;
if( alertExists )
alert = CreateAlertFromXml( xmlRdr );
}
finally
{
if( xmlRdr != null )
xmlRdr.Close();
}
return alert;
}
That method depends on two private helper methods; RetrieveAlertXml
and CreateAlertFromXml
. The former is implemented like so:
static XmlTextReader RetrieveAlertXml()
{
try
{
Uri uri = new Uri( "AlertData.xml", UriKind.Relative );
StreamResourceInfo info = Application.GetResourceStream( uri );
return new XmlTextReader( info.Stream );
}
catch( Exception ex )
{
Debug.WriteLine( "Did not get Alert XML: " + ex );
return null;
}
}
The method which converts the retrieved XML into an Alert
object is seen below:
static Alert CreateAlertFromXml( XmlTextReader xmlRdr )
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load( xmlRdr );
XmlElement alertElem = xmlDoc.DocumentElement;
string title = alertElem.GetAttribute( "Title" );
object content = DeserializeXaml( alertElem.InnerXml );
return new Alert( title, content );
}
The real magic behind converting a string full of XAML into visual elements is implemented like this:
static object DeserializeXaml( string xaml )
{
using( MemoryStream stream = new MemoryStream() )
{
byte[] bytes = Encoding.UTF8.GetBytes( xaml );
stream.Write( bytes, 0, bytes.Length );
stream.Position = 0;
return XamlReader.Load( stream );
}
}
AlertWindow
Last but not least we have the Window
which serves as a dynamic content container and a source of interaction logic. The AlertWindow
class is used as a modal dialog to display the dynamically loaded UI content. Here is a portion of the XAML declaration for AlertWindow
:
<!---->
<Grid Hyperlink.RequestNavigate="OnHyperlinkRequestNavigate">
<!---->
<Grid.CommandBindings>
<CommandBinding
Command="{x:Static ApplicationCommands.Close}"
Executed="OnCloseCommandExecuted"
/>
</Grid.CommandBindings>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!---->
<TextBlock Grid.Row="0"
Background="Gray"
FontSize="23"
Foreground="White"
Text="{Binding Path=Title}"
TextAlignment="Center"
/>
<!---->
<ContentControl Grid.Row="1"
Content="{Binding Path=Content}"
/>
<!---->
<TextBlock Grid.Row="2"
FontSize="15"
HorizontalAlignment="Center"
Margin="6"
>
[<Hyperlink Command="Close">Close</Hyperlink>]
</TextBlock>
</Grid>
As you can see above, the Grid
panel which contains all of the other elements has two interesting settings applied to it. The attached event syntax is used to add a handler for the Hyperlink
class's RequestNaviate
bubbling routed event. This ensures that when the user clicks on a dynamically loaded Hyperlink
the OnHyperlinkRequestNavigate
method in the code-behind will be invoked. The Grid
also has a CommandBinding
established for the Close
command. That binding ensures that the AlertWindow
is notified when the user wants to close it.
Here is the code-behind for AlertWindow
:
public partial class AlertWindow : System.Windows.Window
{
public AlertWindow( Window owner, Alert alert )
{
InitializeComponent();
this.Owner = owner;
this.DataContext = alert;
}
void OnCloseCommandExecuted( object sender, ExecutedRoutedEventArgs e )
{
this.Close();
}
void OnHyperlinkRequestNavigate( object sender, RequestNavigateEventArgs e )
{
Process.Start( e.Uri.AbsoluteUri );
}
}
Conclusion
This article demonstrates the exciting potential of dynamic user interfaces in WPF applications. Through the use of XAML, a content container, and routed interaction notifications we were able to make use of arbitrary UI content at runtime. It is also important to note that the technique described in this article is not the only approach you can use to create dynamic UIs. Before implementing a dynamic UI in your application, be sure to consider other options to make sure that the most appropriate technique is used.