Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / WPF
Article

Dynamic User Interfaces in WPF

Rate me:
Please Sign up or sign in to vote.
4.64/5 (20 votes)
8 Jun 2007CPOL8 min read 217.4K   3.9K   113   28
Reviews how to implement dynamic user interfaces in WPF.

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:

Screenshot of demo application

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:

XML
<?xml version="1.0" encoding="utf-8" ?>
<Alert Title="Company News">
  <!-- The inner XML of the <Alert> element is valid XAML. -->
  <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:

C#
/// <summary>
/// Stores information about an alert.
/// </summary>
public class Alert
{
 readonly object content;
 readonly string title;

 public Alert( string title, object content )
 {
  if( content == null )
   throw new ArgumentNullException( "content" );

  // If the alert's title was not specified use a default value.
  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:

C#
/// <summary>
/// This is the main Window of an imaginary business app.
/// </summary>
public partial class MainWindow : Window
{
 public MainWindow()
 {
  InitializeComponent();

  // When the Window loads, check to see if 
  // there is an alert to display.
  this.Loaded += delegate
  {
   this.CheckForAvailableAlert(); 
  };  
 }

 /// <summary>
 /// Checks to see if an alert needs to be shown.
 /// If so, it shows the alert message in a Window.
 /// </summary>
 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:

C#
/// <summary>
/// This method retrieves an alert to display.  
/// If no alert is available it returns null.
/// </summary>
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:

C#
/// <summary>
/// In this demo we keep it simple and just load some XML
/// out of a resource in this assembly.  In a real app you
/// might want to access a Web service in this method, to 
/// get the latest XML alert message, if one exists.
/// </summary>
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:

C#
/// <summary>
/// Creates an Alert object based on the specified XML.
/// </summary>
/// <param name="xmlRdr">Contains data about an alert.</param>
static Alert CreateAlertFromXml( XmlTextReader xmlRdr )
{
 // Load the XML data into an XmlDocument.
 XmlDocument xmlDoc = new XmlDocument();
 xmlDoc.Load( xmlRdr );

 // Get the root <Alert> element.
 XmlElement alertElem = xmlDoc.DocumentElement;

 // Grab the alert's title off the <Alert> element.
 string title = alertElem.GetAttribute( "Title" );

 // Create visual objects out of the <Alert>
 // element's inner XML (which must be valid XAML).
 object content = DeserializeXaml( alertElem.InnerXml );

 // Bundle up the information we retrieved and return it.
 return new Alert( title, content );
}

The real magic behind converting a string full of XAML into visual elements is implemented like this:

C#
/// <summary>
/// Instantiates and returns the object(s) declared 
/// in the specified XAML.
/// </summary>
/// <param name="xaml">Valid XAML markup text.</param>
static object DeserializeXaml( string xaml )
{
 using( MemoryStream stream = new MemoryStream() )
 {
  // Convert the text into a byte array so that 
  // it can be loaded into the memory stream.
  byte[] bytes = Encoding.UTF8.GetBytes( xaml );

  // Write the XAML bytes into a memory stream.
  stream.Write( bytes, 0, bytes.Length );

  // Reset the stream's current position back 
  // to the beginning so that when it is read 
  // from, the read begins at the correct place.
  stream.Position = 0;

  // Convert the XAML into a .NET object.
  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:

XML
<!-- Add a handler to the RequestNavigate event of Hyperlink
     so that if a dynamically loaded Hyperlink is clicked, 
     we can open the Web page to which it points. -->
<Grid Hyperlink.RequestNavigate="OnHyperlinkRequestNavigate">
  <!-- Associate an event handling method with the Executed 
       event of the Close command. -->
  <Grid.CommandBindings>
    <CommandBinding 
      Command="{x:Static ApplicationCommands.Close}" 
      Executed="OnCloseCommandExecuted" 
      />
  </Grid.CommandBindings>

  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>

  <!-- ALERT TITLE HEADER -->
  <TextBlock Grid.Row="0"
    Background="Gray" 
    FontSize="23" 
    Foreground="White"         
    Text="{Binding Path=Title}" 
    TextAlignment="Center" 
    />

  <!-- ALERT CONTENT CONTAINER -->
  <ContentControl Grid.Row="1"
    Content="{Binding Path=Content}"          
    />

  <!-- [CLOSE] HYPERLINK -->
  <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:

C#
/// <summary>
/// Displays an alert message.
/// </summary>
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Josh creates software, for iOS and Windows.

He works at Black Pixel as a Senior Developer.

Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.

Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.

Check out his Advanced MVVM[^] book.

Visit his WPF blog[^] or stop by his iOS blog[^].

See his website Josh Smith Digital[^].

Comments and Discussions

 
GeneralMy vote of 5 Pin
Champion Chen28-Apr-14 19:29
Champion Chen28-Apr-14 19:29 
QuestionUnknown "StackPanel" Error Pin
Vikasumit11-Sep-13 21:58
professionalVikasumit11-Sep-13 21:58 
AnswerRe: Unknown "StackPanel" Error Pin
Vikasumit11-Sep-13 22:17
professionalVikasumit11-Sep-13 22:17 
GeneralMy vote of 5 Pin
umarinam13-Aug-10 6:49
umarinam13-Aug-10 6:49 
QuestionRealy nice ! but how to in MvvM ??? Pin
Patrick Antonioli Ferraro18-Jun-10 2:35
Patrick Antonioli Ferraro18-Jun-10 2:35 
QuestionUserControls? Pin
adamsd529-Jul-07 20:53
adamsd529-Jul-07 20:53 
GeneralSome Comments [modified] Pin
Marc Clifton10-Jun-07 6:46
mvaMarc Clifton10-Jun-07 6:46 
GeneralRe: Some Comments Pin
Josh Smith10-Jun-07 7:54
Josh Smith10-Jun-07 7:54 
GeneralRe: Some Comments Pin
Marc Clifton10-Jun-07 8:28
mvaMarc Clifton10-Jun-07 8:28 
GeneralRe: Some Comments Pin
Josh Smith10-Jun-07 8:41
Josh Smith10-Jun-07 8:41 
GeneralRe: Some Comments Pin
Marc Clifton10-Jun-07 8:56
mvaMarc Clifton10-Jun-07 8:56 
GeneralRe: Some Comments Pin
AlexY10-Jun-07 16:40
AlexY10-Jun-07 16:40 
GeneralRe: Some Comments Pin
jrdnln11-Jun-07 4:10
jrdnln11-Jun-07 4:10 
Geez, someone takes the time to write a nice demo and article and this Clifton guy jumps all over him for not living up to his holier than though, God's gift to "the programming community" attitude. Sounds like somebody needs to brush up on their people skills and get a life. What a freak.

Jordan
GeneralRe: Some Comments Pin
Marc Clifton11-Jun-07 14:52
mvaMarc Clifton11-Jun-07 14:52 
GeneralRe: Some Comments Pin
Josh Smith11-Jun-07 15:18
Josh Smith11-Jun-07 15:18 
GeneralRe: Some Comments Pin
Thesisus26-Sep-12 2:59
Thesisus26-Sep-12 2:59 
GeneralRe: Some Comments Pin
Scott Dorman24-Sep-07 5:06
professionalScott Dorman24-Sep-07 5:06 
GeneralRe: Some Comments Pin
Scott Dorman24-Sep-07 5:02
professionalScott Dorman24-Sep-07 5:02 
GeneralRe: Some Comments Pin
Josh Smith10-Jun-07 17:53
Josh Smith10-Jun-07 17:53 
GeneralRe: Some Comments Pin
WillemM11-Jun-07 1:59
WillemM11-Jun-07 1:59 
GeneralRe: Some Comments Pin
Josh Smith11-Jun-07 2:27
Josh Smith11-Jun-07 2:27 
GeneralRe: Some Comments Pin
Marc Clifton11-Jun-07 14:56
mvaMarc Clifton11-Jun-07 14:56 
GeneralRe: Some Comments Pin
Josh Smith11-Jun-07 15:10
Josh Smith11-Jun-07 15:10 
QuestionAcropolis? Pin
sdahlbac9-Jun-07 13:58
sdahlbac9-Jun-07 13:58 
AnswerRe: Acropolis? Pin
Josh Smith9-Jun-07 14:19
Josh Smith9-Jun-07 14:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.