The Mysteries of XAML

I’d like to start with a short story. I’m trying to write a simple Hello World program. I want it to run on Android, and it must use Xamarin Forms, and use XAML to describe the UI. Should be easy, 5 minutes to do, right? Not in my case.

I go into Visual Studio and create a blank app (Xamarin.Forms Portable). I then “Add New/Forms Xaml Page”, which creates Page1.xaml, replace the hard-wired C# code for building the UI in App.cs with “MainPage = new Page1();”, compile and try to run the program. Unfortunately, it doesn’t do what I expected: there is no text displayed. Looking at Page1.xaml, I realize that the code “{Binding MainText}”  isn’t working. What the hell did Visual Studio create?

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App1Error.Page1">
  <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Maybe you know–or maybe not, as I didn’t–the explanation is quite simple: If the BindingContext of the XAML-specified UI data structure is null, which is so in this case, there is no object to get the string property “MainText”. Consequently, the Text property of the Label object is initialized to the empty string. There is no runtime error message produced because referencing a property with a null BindingContext is “OK.”

Excuse me, but this is bad. People don’t have a chance in hell if an IDE generates code that doesn’t work. Don’t just take my word on it, others have the same observation. The IDE should generate an empty XAML file, or it should generate stubs for an app that has some structure (MVVM, MVP, MVC, etc).

So, on I go to investigate XAML, because that’s what this is really all about.

What is XAML?

XAML is a language for specifying tree-oriented data structures. Simply stated, XML elements and attributes are mapped into UI classes and properties, respectively. While you can avoid using XAML in your program, people want the UI to have a clean separation from the logic of the program. XAML helps you accomplish this because it is a declarative language.

XAML has a long history. Initially, there was speculation whether it was a replacement of HTML for web browsers, but it was just a language for UI data structures for Microsoft’s desktop. Writing an XAML spec was supposed “to be developed with a visual tool where developers do not even need to understand the underlying markups,” but in the initial release, the tool was terrible. It still is a problem, because there is no designer for Xamarin Forms XAML.

That would be fine, but there is no formal definition of the language to fall back on. If you read Microsoft’s spec on XAML, it is stated that the interpretation is up to individual XAML processors. In other words, the interpretation of the directive is what it is, whatever that is. But, as we shall see, XAML elements map to UI objects, and we can get a good idea what it does by reading the API for Xamarin Forms.

When a program is compiled, linked, and run, the XAML code is embedded in the assembly, then converted at runtime into a data structure by LoadFromXaml. The code that converts the XML to a data structure is, of course, not open sourced, but you can read how it works using a disassembly (e.g., with DotPeek).

I’ll go over several examples which illustrate some of the ways you can specify a view, and connect it up with a model, since that is what you will want to do 99% of the time.

Disassociated View

After creating an app, change the Label Text property assignment with a simple “Hello World”, i.e., a static string. In this case, there is no associated information with the view. Everything in the view is self-contained. I refer to this as a disassociated view.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
             x:Class="App1.Page1">
  <Label Text="Hello World!" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Note, Label is an XML element, but it’s converted into an object of type Xamarin.Forms.Label. Text, VerticalOptions, and HorizontalOptions are XML attributes, properties of the Label object. The declaration x:Class=”App1.Page1″ is a directive that clues the XAML translator to generate C# source code for the class. That’s done while you edit the XAML file in the IDE.

Another important note here. A property of an object can be set one of two ways in XAML:

  • a property element, where the property and assigned value is expressed as a nested XML element of the containing object.
  • a property attribute, where the property and assigned value is expressed as an XML attribute.

Above, you see the property attribute Text. Below, you’ll see property element BindingContext.

View with Binding

Most of the time, a UI needs to get information from a Model. To do that, you need a binding, which is an association of the view to another object. In fact, if the binding is between the UI and an object derived from IObserverableObject, then it can do magic. (Note, for a discussion of MVVM, see So What Exactly is a View-Model?.)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App2;assembly=App2"
             x:Class="App2.Page1">
  <ContentPage.BindingContext>
    <local:ViewModel />
  </ContentPage.BindingContext>
  <Label Text="{Binding MessageOfTheDay}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

A binding consists of (1) a context assignment, and (2) a binding assignment. Note: nowhere are these terms defined in the spec or by anyone else. However, in order to talk about binding, you have to have a terminology. If you are so inclined, you can read how binding is implemented in the MS documentation.

The context assignment in this example is specified in the XML element <ContentPage.BindingContext>…</ContentPage.BindingContext>. In terms of C# code, it would be the same as “BindingContext = …”. Note how the assignment is written–using a contained element.

The binding assignment associates an object with the view: Label=”{Binding MessageOfTheDay}”. When the UI is constructed, a tree walk is performed up the predecessors until a context is found with the property. NB: the property specified in the binding expression must be a property, not a field or method, otherwise the binding fails.

Singletons

Sometimes you may want a binding to be shared between multiple ContentPage, e.g., a model. In this situation, you can use a x:Static extension.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App3;assembly=App3"
             x:Class="App3.Page1"
             BindingContext="{x:Static local:Class1.Singleton}">
  <StackLayout>
    <Label Text="{Binding CurrentValue}" />
    <Label Text="{Binding CurrentValue}" />
  </StackLayout>
</ContentPage>

A singleton property is defined and used in one XAML file.

There is no way to create a singleton tree node, but you can share the tree node using x:Key. XAML extensions are discussed below.

Binding Lists

A list of objects can be consumed by the view. In Xamarin, ListView has the property ItemsSource, which contains an enumerable collection to iterate over and create children. The semantics of setting ItemsSource is not documented, but essentially:

IEnumerable<T> collection = ....;
foreach (T t in collection) {Add(new Label(t));}

If the ListView XAML specifies a <ListView.ItemTemplate>, the ListView will contain a collection of items of the type specified in the template. By default, it creates a Label. See the documentation.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App4;assembly=App4"
             x:Class="App4.Page1">
  <ContentPage.BindingContext>
    <local:Class1/>
  </ContentPage.BindingContext>
  <StackLayout>
    <Label Text="Value"/>
    <ListView ItemsSource="{Binding Values}"></ListView>
  </StackLayout>
</ContentPage>
using System.Collections.Generic;

namespace App4
{
    class Class1
    {
        public List<string> Values { get; private set; }= new List<string>()
        {
            "a", "b", "c"
        };
    }
}

 

Observer/Observable Pattern

After getting used to XAML, at some point, you will want information in the view to update whenever the corresponding model changes while it’s happening. The UI requires the binding object have an INotifyPropertyChanged interface. In the properties that change, OnPropertyChanged() is called to produce the side effect in the view.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App5;assembly=App5"
             x:Class="App5.Page1">
  <ContentPage.BindingContext>
    <local:Class1/>
  </ContentPage.BindingContext>
  <StackLayout>
    <Label Text="{Binding FirstName}"/>
    <Label Text="{Binding LastName}"/>
    <Label Text="{Binding CurrentDateTime}"/>
  </StackLayout>
</ContentPage>
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using App5.Annotations;

namespace App5
{
    class Class1 : INotifyPropertyChanged
    {
        public Class1()
        {
            // Set up timer to tick of current time.
            _timer = new Timer((o) =>
            {
                Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
                {
                    CurrentDateTime = DateTime.Now.ToString();
                    LastName = "You should never see this because OnPropertyChanged() is not called!";
                });
            }, null, 0, 1000);
        }

        private Timer _timer;
        public String FirstName { get; set; } = "hi";
        public String LastName { get; set; } = "there";
        private String _current_date_time = DateTime.Now.ToString();
        public String CurrentDateTime
        {
            get
            {
                return _current_date_time;
            }
            set
            {
                _current_date_time = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

 

View and Commands

There are two schools for handling events when the user clicks a button or enters text in a data text box. The “old school” is to add an event handler to the code-behind file to make some response, e.g., the OnButtonClicked() override. (In fact, Xamarin shows how you would do that in their last example of this introduction to XAML.) The "new school” is to add a Command property assignment to the XAML, and add the handler into the “ViewModel”. (Note, as someone else points out, placing all the handlers in the ViewModel reduces it to a code behind file.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App6;assembly=App6"
             x:Class="App6.Page1">
  <ContentPage.BindingContext>
    <local:Class1/>
  </ContentPage.BindingContext>
  <StackLayout>
    <Button Text="Click me" Command="{Binding Click}"/>
    <Label Text="{Binding MessageOfTheDay}" />
  </StackLayout>
</ContentPage>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;

namespace App6
{
    public class Class1 : INotifyPropertyChanged
    {
        public Class1()
        {
            _message_of_the_day = _messages[_i];
            Click = new Command((nothing) =>
            {
                DoWhop();
            });
        }

        private String _message_of_the_day;

        public String MessageOfTheDay
        {
            get
            {
                return _message_of_the_day;
            }
            private set
            {
                _message_of_the_day = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private int _i = 0;

        private List<String> _messages = new List<string>()
        {
            "What's happening?",
            "This is cool, huh?",
            "Working for the weekend.",
            "No time like the present.",
            "Time flies like an arrow.",
            "Microsoft and Xamarin XAML is the best thing since sliced bread!"
        };

        public ICommand Click { protected set; get; }

        private void DoWhop()
        {
            _i++;
            if (_i >= _messages.Count)
                _i = 0;
            MessageOfTheDay = _messages[_i];
        }
    }
}

 

Debugging XAML

Binding problems can be resolved sometimes by modifying the binding assignment. At the moment, there doesn’t seem to be a way to check if a context is null in Xamarin Forms. In WPF, it would be accomplished using PresentationTraceSources: add a trace statement, Text=”{Binding MessageOfTheDay, diag:PresentationTraceSources.TraceLevel=High}”; define the namespace “diag”, xmlns:diag=”clr-namespace:System.Diagnostics;assembly=WindowsBase”.

If you have a non-null binding context, you can debug the binding when it’s accessed, either in the property or with a data converter.

XAML Extensions

XAML has a special namespace for extensions to the XAML processor. We’ve already seen some of those, e.g., x:Class.

 

  • x:Class – generate a C# class with the given name.
  • x:Name – generate a C# property.
  • x:Reference – use a C# property. Goes in hand with x:Name.
  • x:Key – create a value, in the context of a resource.

An example containing some of these directives follows.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:App6;assembly=App6"
             x:Class="App6.Page1">
  <ContentPage.Resources>
    <ResourceDictionary>
      <x:String x:Key="whatever">3</x:String>
    </ResourceDictionary>
  </ContentPage.Resources>
  <StackLayout>
    <Label Text="{StaticResource whatever}"/>
    <Label Text="fun" x:Name="foobar1"/>
  </StackLayout>
</ContentPage>

 

Conclusions

XAML is an expressive tree-oriented data structure language for UI. Unfortunately, there isn’t a specification of the language, but there are some well-known idioms to help you use it effectively. Enjoy!

Additional Information

Nathan, Adam. XAML unleashed. Sams Publishing, 2014.

As usual, I provide the source for all examples, here in git.

 

 

Posted in Tip