Loading dynamic Xaml with template support in Windows Phone
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:
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.
The example can be downloaded here.
This is a template that is used to display train information for a metro
train system. It's a one-to-many relationship between the station (here
is example as the binding Station [4]) and information about several trains that will depart from this station. To display this relationship have I created a ItemsControl [6], that binds to the collection Items [6] and for each item do I display the property Next [17] and Later [18].
Reading of the XML file is encapsulated in a method named loadXamlFromFile. It returns string because XmlReader.Load reads a string.
And store it in a variable with the name of vm, for ViewModel.
[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.
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.
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
The xml file look like this
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: }
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 ObservableCollectionItems { get; set; }
5: }
6:
7: public class TraficInformation
8: {
9: public string Next { get; set; }
10: public string Later { get; set; }
11: }
I fill these classes with the following code:
1: StationData vm = new StationData()
2: {
3: Station = "Humlegården",
4: Items = new ObservableCollection()
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: };
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()
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: }
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