Practical .NET

Construct XAML Forms at Runtime with Resource Files

WPF makes it very easy to load non-executable resources at run time -- including a complete UI in XAML. Here's how to leverage that functionality to create applications that you can customize without recompiling.

Sometimes different users need different UIs, or different sites where your application is installed need different UIs. You could add a bunch of logic that makes controls visible or invisible as needed (and best of luck testing that). Or you could just load the UI that your user or site needs. In that scenario, the ultimate form contains no content:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">

</Window>

At runtime, you'll now need to load the UI based on settings in a config file or the preferences of the currently logged on user. To support that, you can define each of those UIs in your XAML file, then paste each of them into a separate XML file. This example, in a file called MyGrid.xml, defines a Grid containing a DataGrid and two buttons:

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DataGrid Name="DataGrid1" … />
    <Button Name="UpdateBtn" … />
    <Button Content="DeleteBtn" … />
  </Grid>

By default, any XML file you add to your project is a resource file; and when your file absolutely, positively has to be present, a resource file is your best choice. A resource file is compiled into your executable and guaranteed to be distributed with it. If you have a limited number of configurations, you can set them up as individual resource files and compile them all into your executable.

However, if you don't want the file to be incorporated into your executable, you should select the XML file in Solution Explorer and set its Build Action property to Content. A content file won't be distributed with your application (at least if you use any of the standard distribution methods). Instead, it will be a separate file you must copy to the folder holding your executable, but can then replace or modify at your convenience.

To ensure that the file is copied to the folder with your executable (this makes referring to the resource in your code easier), set the file's Copy To Output Directory property. To ensure you get the most recent version of the file, set the file's Copy To Output Directory to Copy If Newer.

Accessing a Resource File
Now you're ready to write the code to load the UI appropriate for your user or installation. First you need to create a StreamResourceInfo class, passing the URI for your resource file. Next, create a XamlReader to pull the XAML from the file.

You can then call the XamlReader's LoadAsync method, passing the Stream property of your StreamResoureInfo object. You'll need to cast the output of the LoadAsync method to the kind of resource you're pulling from the file. For this example, since the file contains a Grid, the output must be cast as a Grid. This code in the form's Window Loaded event does the trick for a resource file:

Dim GridUri As New Uri("/MyGrid.xml", UriKind.Relative)
Dim sri As Windows.Resources.StreamResourceInfo =
         Application.GetResourceStream(GridUri)
Dim xrdr As New System.Windows.Markup.XamlReader()
Dim grd As Grid = CType(xrdr.LoadAsync(sri.Stream), Grid)
Me.Content = grd

The code is only slightly different if you've used a Content file: Instead of using the Application's GetResourceStream method, use its GetContentStream method.

Of course, you'll want to gain access to the controls you've just loaded. Since this code has a variable pointing to the Grid that holds the controls, you can use the Grid's Children collection to access each of the controls the Grid holds. This code accesses the second control (a button) and wires up an event handler for its Click event:

Dim UpdBtn As Button
  UpdBtn = CType(grd.Children(1), Button)
  AddHandler UpdBtn.Click, AddressOf DoUpdate

After deploying your application, you'll be able to modify or replace to update or customize your application -- if you've set your XML files up as a content file. While replacing your whole UI is an extreme example, you could just as easily use this technique with the Content property of a Frame that defines part of your page. Either way, you can eliminate a bunch of control logic that modifies your UI with a half-dozen lines of code that just reads in the right UI.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.