Introduction: This is a specialized approach that makes sense in certain
scenarios where we need highly dynamic user interfaces. We load part of the user
interface from a XAML file at runtime using the XamlReader class from the
System.Windows.Markup namespace.
One of the most interesting ways to use XAML is to parse it on the fly with the
XamlReader.
Let us create a project and load external XAML by using XamlReader in some other
XAML window.
Step 1: Add a window application and name is NonCompiledXaml
Step 2: Open Window1.xaml and delete what visual studio adds up and write
below code;
<DockPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button
Name="button1"
Margin="30">Please
click me</Button>
</DockPanel>
Here I have just created a simple button inside a dockpanel.
Step 3: At runtime we can load this content in a live window to create
the same window. So open window1.xmal.cs and write the below code.
public
partial class
Window1 : Window
{
private
Button button1;
public Window1(string
xamlFile)
{
InitializeComponent(xamlFile);
}
private void
InitializeComponent(string xamlFile)
{
//Configure the form
this.Width =
this.Height = 285;
this.Left =
this.Top = 100;
this.Title =
"Dynamically Loaded XAML";
//Load the XAML content from external file
DependencyObject
rootElement;
using (FileStream
fs = new FileStream(xamlFile,FileMode.Open))
{
rootElement = (DependencyObject)XamlReader.Load(fs);
}
//Insert the markup into this window.
this.Content = rootElement;
FrameworkElement
frameworkElement = (FrameworkElement)rootElement;
button1 = (Button)frameworkElement.FindName("button1");
//Write up the event handler
button1.Click+=new
RoutedEventHandler(button1_Click);
}
private void
button1_Click(object sender,
RoutedEventArgs e)
{
button1.Content = "Thank you.";
}
}
Here is the explaination of the code:
-
Here, the InitializeComponent() method opens a
FileStream on the Window1.xml file. It then uses the Load() method of the
XamlReader to convert the content in this file into a DependencyObject,
which is the base from which all WPF controls derive. This Dependency-
Object can be placed inside any type of container in this example it's used
as the content for the entire form.
-
To manipulate the button, we need to find the
corresponding control object in the dynamically loaded content. The
LogicalTreeHelper serves this purpose because it has the ability to search
an entire tree of control objects, digging down as many layers as necessary
until it finds the object with the name we've specified. An event handler is
then attached to the Button.Click event.
-
Another alternative is to use the
FrameworkElement.FindName() method. In this example, the root element is a
DockPanel object. Like all the controls in a WPF window, DockPanel derives
from FrameworkElement. That means we can replace this code;
button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement,
"button1");
This is equivalent to below code;
FrameworkElement
frameworkElement = (FrameworkElement)rootElement;
button1 = (Button)frameworkElement.FindName("button1");
Step 4: Let us add a class program.cs to
write application statup.
public
class Program:Application
{
[STAThread]
static void
Main()
{
Program app =
new Program();
app.ShutdownMode = ShutdownMode.OnLastWindowClose;
//Our first approach: window with XAML Content
Window1 win =
new Window1("Window1.xaml");
win.Show();
}
}
Step 5: Run the application and see
that now we dynamically loading.
Step 6: Here we saw that we're loading an element—the DockPanel
object—from the XAML file. Alternatively, we could load an entire XAML window In
this case, we would cast the object returned by XamlReader.Load() to the Window
type and then call its Show() or ShowDialog() method to show it.
So let create a window and load entire XAML window and see how we can cast it.
Add a window and name it as Xaml2009.xaml
Step 7: Write below class inside Xaml2011.xaml. Delete all default XAML
added by visual studio.
namespace
NonCompiledXaml
{
public class
Xaml2009Window :
Window
{
public static
Xaml2009Window LoadWindowFromXaml(string
xamlFile)
{
// Get the XAML content from an external
file.
using (FileStream
fs = new FileStream(xamlFile,
FileMode.Open))
{
Xaml2009Window window = (Xaml2009Window)XamlReader.Load(fs);
return window;
}
}
private void
lst_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
MessageBox.Show(e.AddedItems[0].ToString());
}
}
public class
Person
{
public string
FirstName { get; set;
}
public string
LastName { get; set;
}
public Person(string
firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public override
string ToString()
{
return FirstName +
" " + LastName;
}
}
}
In this case, we would cast the object returned by XamlReader.Load() to the
Window type and then call its Show() or ShowDialog() method to show it.
Step 8: Add below XAML in Xaml2009.xaml
<local:Xaml2009Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:NonCompiledXaml;assembly=NonCompiledXaml"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Width="300"
Height="300"
Title="XAML 2009">
<StackPanel
Margin="10">
<Label
Target="{x:Reference
txtFirstName}">_FirstName</Label>
<TextBox
x:Name="txtFirstName"
/>
<Label
Margin="0,10,0,0"
Target="{x:Reference
txtLastName}">_LastName</Label>
<TextBox
x:Name="txtLastName"
/>
<ListBox
Margin="0,25,0,0"
SelectionChanged="lst_SelectionChanged">
<x:String>Item
One</x:String>
<x:String>Item
Two</x:String>
<x:String>Item
Three</x:String>
<local:Person>
<x:Arguments>
<x:String>Joe</x:String>
<x:String>McDowell</x:String>
</x:Arguments>
</local:Person>
<sys:Guid
x:FactoryMethod="NewGuid"></sys:Guid>
</ListBox>
</StackPanel>
</local:Xaml2009Window>
Step 9: Update program.cs
public
class Program
: Application
{
[STAThread()]
static void
Main()
{
// Create a program that will show two
different windows,
// and will wait until both are
closed before ending.
Program app =
new Program();
app.ShutdownMode = ShutdownMode.OnLastWindowClose;
// First approach: window with XAML content.
Window1 window1 =
new Window1("Window1.xaml");
window1.Show();
// Second approach: XAML for complete
window.
Xaml2009Window window2 =
Xaml2009Window.LoadWindowFromXaml("Xaml2009.xaml");
window2.Show();
app.Run();
}
}
Step 10: Run the application
When to use this approach : For example, we could create an all-purpose survey
application that reads a form file from a web service and then displays the
corresponding survey controls (labels, text boxes, check boxes, and so on). The
form file would be an ordinary XML document with WPF tags, which we load into an
existing form using the XamlReader. To collect the results once the survey is
filled out, we simply need to enumerate over all the input controls and grab
their content.
Conclusion: Obviously, loading XAML dynamically won't be as efficient as
compiling the XAML to BAML and then loading the BAML at runtime, particularly if
our user interface is complex.
However, it opens up a number of possibilities for building dynamic user
interfaces.
Thanks for reading