I have thought that in the future to write more posts in English. I know that all my Swedish readers have no problem with it.
Sometimes you want more flexibility to adapt the user interface for the data you download from a Web service. With dynamic loading of XAML one can both send data and a description of how this data should be displayed. With templates, you do not build the user interface and insert the data in the on the server side. One can instead bind the user interface and the data on the client side.
In this article I will show you how to load XAML from a local xml file. (In reality, this xml obviously come from a Web service.) I also want to show you how to get bindings to work to show the data.
The important method to get this to work is called XamlReader.Load, which takes a string of valid XAML code and creates an object structure of it.
With valid XAML code means:
- The XAML content string must define a single root element.
- The content string XAML must be well formed XML, as well as being parsable XAML.
- The required root element must overpriced specify a default XML namespace value. This is typically the Windows Phone namespace, xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation". This XML namespace is explicitly required in Windows Phone.
One can make use of well-formed XML to describe XAML. In my example, it means that I create a standard XML document and this creates my XAML.
I would emphasize again that one needs to have the attribute xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation in the root element object.
1: <?xml version="1.0" encoding="utf-8" ?>
2: <Border xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
3: ...
4: </Border>
Example
The example can be downloaded here.
XML file
1: <Border xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Padding="10, 7, 10, 10" BorderBrush="#FF4F6261" BorderThickness="1.5">
2: <StackPanel>
3: <Border Background="#FF070709" CornerRadius="2" BorderThickness="2" BorderBrush="#FF2D3A3A">
4: <TextBlock Text="{Binding Station}" Foreground="#FFFBFDFE" TextWrapping="Wrap" FontWeight="Bold" TextAlignment="Center" />
5: </Border>
6: <ItemsControl ItemsSource="{Binding Items}">
7: <ItemsControl.ItemTemplate>
8: <DataTemplate>
9: <Grid>
10: <Grid.ColumnDefinitions>
11: <ColumnDefinition Width="10"/>
12: <ColumnDefinition Width="*"/>
13: </Grid.ColumnDefinitions>
14: <Rectangle Grid.Column="0" Fill="LightGreen" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,13,4,4" />
15: <Border Grid.Column="1" Margin="0,10,0,0">
16: <TextBlock Foreground="#FFD6CB46" FontWeight="Bold" TextAlignment="Left" FontFamily="Segoe WP Bold" Padding="4" FontSize="15">
17: <Run Text="{Binding Next}"/>
18: <LineBreak/>
19: <Run Text="{Binding Later}"/>
20: </TextBlock>
21: </Border>
22: </Grid>
23: </DataTemplate>
24: </ItemsControl.ItemTemplate>
25: </ItemsControl>
26: </StackPanel>
27: </Border>
Read XML file
1: private string loadXamlFromFile()
2: {
3: StreamResourceInfo strm = Application.GetResourceStream(new Uri("xamltemplates/metro.xml", UriKind.Relative));
4:
5: XElement x = XElement.Load(strm.Stream);
6:
7: return x.ToString();
8: }
Reading of the XML file is encapsulated in a method named loadXamlFromFile. It returns string because XmlReader.Load reads a string.
Create the data structuer that will be bind to the template
This is done in a simple way. The information consists of two classes, one of the station (StationData) and one for the traffic (TraficInformation).
1: public class StationData
2: {
3: public string Station { get; set; }
4: public ObservableCollection<TraficInformation> Items { get; set; }
5: }
6:
7: public class TraficInformation
8: {
9: public string Next { get; set; }
10: public string Later { get; set; }
11: }
1: StationData vm = new StationData()
2: {
3: Station = "Humlegården",
4: Items = new ObservableCollection<TraficInformation>()
5: {
6: new TraficInformation() { Next = "18 Farsta Strand 1 min", Later="19 Hagsätra 5 min"},
7: new TraficInformation() { Next= "19 Hässelby str.", Later = "18 Vällingby 5 min, 19 Hässelby str. 8 min"}
8: }
9: };
And store it in a variable with the name of vm, for ViewModel.
Do the magic
The app has an event handler registered (commandLoadDynamicUI) for a buttons click event.
1: private void commandLoadDynamicUI_Click(object sender, RoutedEventArgs e)
2: {
3: string metroTemplate = loadXamlFromFile();
4: var metroXaml = XamlReader.Load(metroTemplate);
5:
6: StationData vm = new StationData()
7: {
8: Station = "Humlegården",
9: Items = new ObservableCollection<TraficInformation>()
10: {
11: new TraficInformation() { Next = "18 Farsta Strand 1 min", Later="19 Hagsätra 5 min"},
12: new TraficInformation() { Next= "19 Hässelby str.", Later = "18 Vällingby 5 min, 19 Hässelby str. 8 min"}
13: }
14: };
15:
16: (metroXaml as FrameworkElement).DataContext = vm;
17: gridOfContent.Children.Add(metroXaml as UIElement);
18: }
[3] call the method that loads the XML file and returns a string which is saved into the variable metroTemplate. At row [4] we call XamlReader.Load with that string and we get an Object variable is returned. [6 - 14] is the loading of the data that I described before.
At row [17] I add the XAML object structure, that XamlReader.Load returned, into an existing object tree. I have chosen to use a Grid as this object, but you can use any xaml objects that have a Children property.
Row [16] sets the DataContext for the root element object that was defined in the XML file.
Comments