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

Featured

  • Copilot Billing Shock Hits Developers

    Developer complaints about GitHub Copilot's new usage-based billing model have centered on unexpectedly rapid AI credit consumption, and neither GitHub nor Microsoft has responded directly to the backlash, though they have previously published guidance to lessen model usage costs.

  • Hands On with GitHub Copilot App Technical Preview: Turning a Blazor Issue into a PR

    GitHub's brand-new Copilot desktop app, in technical preview, handled a small Blazor issue from planning through pull request creation, but the hands-on test also showed why developers still need to verify agent work in the running app before merging.

  • At Build 2026, Microsoft Sets Up Windows as an OS for AI Agents

    Microsoft's Build 2026 Windows developer announcements point to a broader platform strategy for agentic AI, spanning terminal workflows, local models, app-building skills, Cloud PCs and operating system-level containment.

  • Slammed by Copilot Usage-Based Billing on Day 1, Facing $180 Bill for June

    A journalist using GitHub Copilot Pro details how a broken editorial workflow on day one of usage-based billing led to runaway token consumption, a projected $180 monthly bill, and practical tactics for cutting AI credit burn.

Subscribe on YouTube