35 min read

(For more resources related to this topic, see here.)

XAML

C++ Store applications typically use eXtensible Application Markup Language (XAML) as the main language for creating the user interface. The first question that comes to mind when XAML is first mentioned, is why? What’s wrong with C++, or any other existing programming language?

XAML is an XML-based language that describes the what, not the how; it’s declarative and neutral. Technically, a complete app can be written without any XAML; there’s nothing XAML can do that C++ can’t. Here are some reasons why XAML makes sense (or at least may make sense in a little bit):

  • C++ is very verbose as opposed to XAML. XAML is usually shorter than the equivalent C++ code.

  • Since XAML is neutral, design-oriented tools can read and manipulate it. Microsoft provides the Expression Blend tool just for this purpose.

  • The declarative nature of XAML makes it easier (most of the time, after users get used to it) to build user interfaces, as these have a tree-like structure, just like XML.

XAML itself has nothing to do with the user interface in itself. XAML is a way to create objects (usually an object tree) and set their properties. This works for any type that is “XAML friendly”, meaning it should have the following:

  • A default public constructor

  • Settable public properties

The second point is not a strict requirement, but without properties the object is pretty dull.

XAML was originally created for Windows Presentation Foundation (WPF), the main rich client technology in .NET. It’s now leveraged in other technologies, mostly in the .NET space, such as Silverlight and Windows Workflow Foundation (WF).

The XAML level currently implemented in WinRT is roughly equivalent to Silverlight 3 XAML. In particular, it’s not as powerful as WPF’s XAML.

XAML basics

XAML has a few rules. Once we understand those rules, we can read and write any XAML. The most fundamental XAML rules are as follows:

  • An XML element means object creation

  • An XML attribute means setting a property (or an event handler)

With these two rules, the following markup means creating a Button object and setting its Content property to the string Click me:

<Button Content="Click me!" />

The equivalent C++ code would be as follows:

auto b = ref new Button;
b->Content = "Click me";

When creating a new Blank App project, a MainPage.xaml file is created along with the header and implementation files. Here’s how that XAML file looks:

<Page
x:Class="BasicXaml.MainPage"





mc:Ignorable="d">
<Grid Background="{StaticResource
ApplicationPageBackgroundThemeBrush}">
</Grid>
</Page>

It’s worth going over these lines in detail. In this example, the project name is BasicXaml. The root element is Page and an x:Class attribute is set, indicating the class that inherits from Page, here named BasicXaml::MainPage. Note that the class name is the full name including namespace, where the separator must be a period (not the C++ scope resolution operator ::). x:Class can only be placed on the root element.

What follows that root element is a bunch of XML namespace declarations. These give context to the elements used in the entire XAML of this page. The default XML namespace (without a name) indicates to the XAML parser that types such as Page, Button, and Grid can be written as they are, without any special prefix. This is the most common scenario, because most of the XAML in a page constitutes user interface elements.

The next XML namespace prefix is x and it points to special instructions for the XAML parser. We have just seen x:Classin action. We’ll meet other such attributes later in this article.

Next up is a prefix named local, which points to the types declared in the BasicXaml namespace. This allows creating our own objects in XAML; the prefix of such types must be local so that the XAML parser understands where to look for such a type (of course, we can change that to anything we like). For example, suppose we create a user control derived type named MyControl. To create a MyControl instance in XAML, we could use the following markup:

<local:MyControl />

The d prefix is used for designer-related attributes, mostly useful with Expression Blend. The mc:ignorable attribute states that the d prefix should be ignored by the XAML parser (because it’s related to the way Blend works with the XAML).

The Grid element is hosted inside the Page, where “hosted” will become clear in a moment. Its Background property is set to {StaticResource ApplicationPageBackgroundThemeBrush}. This is a markup extension, discussed in a later section in this article.

XAML is unable to invoke methods directly; it can just set properties. This is understandable, as XAML needs to remain declarative in nature; it’s not meant as a replacement for C++ or any other programming language.

Type converters

XML deals with strings. However, it’s clear that many properties are not strings. Many can still be specified as strings, and still work correctly thanks to the type converters employed by the XAML parser. Here’s an example of a Rectangle element:

<Rectangle Fill="Red" />

Presumably, the Fill property is not of a string type. In fact, it’s a Brush. Red here really means ref new SolidColorBrush(Colors::Red). The XAML parser knows how to translate a string, such as Red (and many others) to a Brush type (in this case the more specific SolidColorBrush).

Type converters are just one aspect of XAML that make it more succinct than the equivalent C++ code.

Complex properties

As we’ve seen, setting properties is done via XML attributes. What about complex properties that cannot be expressed as a string and don’t have type converters? In this case, an extended syntax (property element syntax) is used to set the property. Here’s an example:

<Rectangle Fill="Red">
<Rectangle.RenderTransform>
<RotateTransform Angle="45" />
</Rectangle.RenderTransform>
</Rectangle>

Setting the RenderTransform property cannot be done with a simple string; it must be an object that is derived from the Transform class (RotateTransform in this case).

The preceding markup is equivalent to the following C++ code:

auto r = ref new Rectangle;
r->Fill = ref new SolidColorBrush(Colors::Red);
auto rotate = ref new RotateTransform();
rotate->Angle = 45;
r->RenderTransform = rotate;

Dependency properties and attached properties

Most propertieson various elements and controls are not normal, in the sense that they are not simple wrappers around private fields. It’s important to realize that there is no difference in XAML between a dependency property and a regular property; the syntax is the same. In fact, there is no way to tell if a certain property is a dependency property or not, just by looking at its use in XAML.

Dependency properties provide the following features

  • Change notifications when the property value changes
  • Visual inheritance for certain properties (mostly the font-related properties)
  • Multiple providers that may affect the final value (one wins out)
  • Memory conservation (value not allocated unless changed)

Some WinRT features, such as data binding, styles, and animations are dependent on that support.

Another kind of dependency properties is attached properties. But essentially an attached property is contextual—it’s defined by one type, but can be used by any type that inherits from DependencyObject (as all elements and controls do). Since this kind of property is not defined by the object it’s used on,it merits a special syntax in XAML. The following is an example of a Canvaspanel that holds two elements:

<Canvas>
<Rectangle Fill="Red" Canvas.Left="120" Canvas.Top="40"
Width="100" Height="50"/>
<Ellipse Fill="Blue" Canvas.Left="30" Canvas.Top="90"
Width="80" Height="80" />
</Canvas>

The Canvas.Left and Canvas.Top are attached properties. They were defined by the Canvas class, but they are attached to the Rectangle and Ellipse elements. Attached properties only have meaning in certain scenarios. In this case, they indicate the exact position of the elements within the canvas .The canvas is the one that looks for these properties in the layout phase.This means that if those same elements were placed in, say a Grid, those properties would have no effect, because there is no interested entity in those properties (there is no harm in having them, however). Attached properties can be thought of as dynamic properties that may or may not be set on objects.

This is the resulting UI:

Setting an attached property in code is a little verbose. Here’s the equivalent C++ code for setting the Canvas.Left and Canvas.Top properties on an element named _myrect:

Canvas::SetLeft(_myrect, 120);
Canvas::SetTop(_myrect, 40);

Content properties

The relationship between a Page object and a Grid object is not obvious. Grid seems to be inside the Page. But how would that translate to code? The Page/Grid markup can be summed up as follows (ignoring the detailed markup):

<Page>
<Grid Background="...">
</Grid>
</Page>

This is actually a shortcut for the following markup:

<Page>
<Page.Content>
<Grid Background="...">
</Grid>
</Page.Content>
</Page>

This means the Grid object is set as the Content property of the Page object; now the relationship is clear. The XAML parser considers certain properties (no more than one per type hierarchy) as the default or content properties. It doesn’t have to be named Content, but it is in the Page case. This attribute is specified in the control’s metadata using the Windows::UI::Xaml::Markup::ContentAttribute class attribute. Looking at the Visual Studio object browser for the Page class shows no such attribute. But Page inherits from UserControl; navigating to UserControl, we can see the attribute set:

Attributes are a way to extend the metadata for a type declaratively. They can be inserted in C++/CX by an attribute type name in square brackets before the item that attribute is applied to (can be a class, interface, method, property, and other code element). An attribute class must derive from Platform::Metadata::Attribute to be considered as such by the compiler.

Some of the common ContentProperty properties in WinRT types are as follows:

  • Content of ContentControl (and all derived types)

  • Content of UserControl

  • Children of Panel (base class for all layout containers)

  • Items of ItemsControl (base class for collection-based controls)

  • GradientStops of GradientBrush (base class of LinearGradientBrush)

Collection properties

Some properties are collections (of type IVector<T> or IMap<K,V>, for instance).Such properties can be filled with objects, and the XAML parser will call the IVector<T>::Append or IMap<K,V>::Insert methods. Here’s an example for a LinearGradientBrush:

<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush EndPoint="1,0">
<GradientStop Offset="0" Color="Red" />
<GradientStop Offset=".5" Color="Yellow" />
<GradientStop Offset="1" Color="Blue" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>

Two rules are at work here. The first is the ContentProperty of LinearGradientBrush (GradientStops), which need not be specified. It’s of the type GradientStopCollection, which implements IVector<GradientStop>, and thus is eligible for automatic appending. This is equivalent to the following code:

auto r = ref new Rectangle;
auto brush = ref new LinearGradientBrush;
brush->EndPoint = Point(1.0, 0);
auto stop = ref new GradientStop;
stop->Offset = 0; stop->Color = Colors::Red;
brush->GradientStops->Append(stop);
stop = ref new GradientStop;
stop->Offset = 0.5; stop->Color = Colors::Yellow;
brush->GradientStops->Append(stop);
stop = ref new GradientStop;
stop->Offset = 1; stop->Color = Colors::Blue;
brush->GradientStops->Append(stop);
r->Fill = brush;

This is perhaps the first clear sign of XAML syntax advantage over C++. Here’s the rectangle in all its glory:

In the case of IMap<K,V>, an attribute named x:Key must be set on each item to indicate the key sent to the IMap<K,V>::Insert method. We’ll see an example of such a map later in this article, when we will discuss resources.

Markup extensions

Markup extensions are special instructions to the XAML parser that provide ways of expressing things that are beyond object creation or setting some property. These instructions are still declarative in nature, but their code equivalent usually entails calling methods, which is not directly possible in XAML.

Markup extensions are placed inside curly braces as property values. They may contain arguments and properties, as we’ll see in later chapters. The only markup extension used by default in a blank page is {StaticResource}, which will be discussed later in this article.

WPF and Silverlight 5 allow developers to create custom markup extensions by deriving classes from MarkupExtension. This capability is unavailable in the current WinRT implementation.

One simple example of a markup extension is {x:Null}. This is used in XAML whenever the value nullptr needs to be specified, as there’s no better way to use a string for this. The following example makes a hole in the Rectangle element:

<Rectangle Stroke="Red" StrokeThickness="10" Fill="{x:Null}" />

Naming elements

Objects created through XAML can be named using the x:Name XAML attribute. Here’s an example:

<Rectangle x_Name="r1">

</Rectangle>

The net result is a private member variable (field) that is created by the XAML compiler inside MainPage.g.h (if working on MainPage.xaml):

private: ::Windows::UI::Xaml::Shapes::Rectangle^ r1;

The reference in itself must be set in the implementation of MainPage::InitializeComponent with the following code:

// Get the Rectangle named 'r1'
r1 = safe_cast<::Windows::UI::Xaml::Shapes::Rectangle^>(
static_cast<Windows::UI::Xaml::IFrameworkElement^>(
this)->FindName(L"r1"));

The mentioned file and method are discussed further in the section XAML compilation and execution. Regardless of how it works, r1 is now a reference to that particular rectangle.

Connecting events to handlers

Events can be connected to handlers by using the same syntax as setting properties, but in this case the value of the property must be a method in the code behind class with the correct delegate signature.

Visual Studio helps out by adding a method automatically if Tab is pressed twice after entering the event name (in the header and implementation files). The default name that Visual Studio uses includes the element’s name (x:Name) if it has one, or its type if it doesn’t, followed by an underscore and the event name, and optionally followed by an underscore and an index if duplication is detected. The default name is usually not desirable; a better approach that still has Visual Studio creating the correct prototype is to write the handler name as we want it, and then right-click on the handler name and select Navigate to Event Handler. This has the effect of creating the handler (if it does not exist) and switching to the method implementation.

Here’s an example of an XAML event connection:

<Button Content="Change" Click="OnChange" />

And the handler would be as follows (assuming the XAML is in MainPage.xaml):

void MainPage::OnChange(Platform::Object^ sender, Windows::UI::Xaml::R
outedEventArgs^ e)
{
}

Visual Studio also writes the namespace name in front of the class name (deleted in the preceding code example); this can be deleted safely, since an in-use namespace statement exists at the top of the file for the correct namespace. Also, the usage of Platform::Object instead of just Object (and similarly for RoutedEventArgs) is less readable; the namespace prefixes can be removed, as they are set up at the top of the file by default.

All events (by convention) use delegates that are similar. The first argument is always the sender of the event (in this case a Button) and the second parameter is the extra information regarding the event. RoutedEventArgs is the minimum type for events, known as routed events. A detailed discussion of routed events is covered in the next article.

XAML rules summary

This is a summary of all XAML rules:

  • An XAML element means creating an instance.

  • An XAML attribute sets a property or an event handler. For properties, a type converter may execute depending on the property’s type.

  • Complex properties are set with the Type.Property element syntax.

  • Attached properties are set with the Type.Property syntax, where Type is the declaring type of the attached property.

  • ContentPropertyAttribute sets a Content property that need not be specified.

  • Properties that are collections cause the XAML parser to call Append or Insert, as appropriate, automatically.

  • Markup extensions allow for special (predefined) instructions.

Introducing the Blend for Visual Studio 2012 tool

Visual Studio 2012 is installed with the Blend for Visual Studio 2012 tool. This tool is typically used by UI designers to create or manipulate the user interface for XAML-based applications.

The initial release of Blend for Visual Studio 2012 only supported Windows 8 Store Apps and Windows Phone 8 projects. The support for WPF 4.5 and Silverlight was added in Update 2 for Visual Studio 2012.

Blend can be used alongside Visual Studio 2012, as both understand the same file types (such as solution .sln files). It’s not atypical to switch back and forth between the two tools—using each tool for its strengths. Here’s a screenshot of Blend with the CH03.sln solution file open (the solution that holds all the samples for this article):

The preceding screenshot shows a particular XAML file open, with one button selected. Several windows comprise Blend, some of which are similar to their Visual Studio counterparts, namely Projects and Properties. Some of the new windows include:

  • Assets: Holds the elements and controls available in WinRT (along with some other useful shortcuts)

  • Objects and Timeline: Include all objects in the visual tree and also animations

  • Resources: Holds all resources (refer to the next section) within the application

Blend’s design surface allows manipulating elements and controls, which is also possible to do in Visual Studio. Blend’s layout and some special editing features make it easier for UI/graphic designers to work with as it mimics other popular applications, such as Adobe Photoshop and Illustrator.

Any changes made using the designer are immediately reflected by the changed XAML. Switching back to Visual Studio and accepting the reload option synchronizes the files; naturally, this can be done both ways.

It’s possible to work from within Blend entirely. Pressing F5 builds and launches the app in the usual way. Blend, however, is not Visual Studio, and breakpoints and other debugging tasks are not supported.

Blend is a non-trivial tool, and is well beyond the scope of this book. Experimentation can go a long way, however.

XAML compilation and execution

The XAML compiler that runs as part of the normal compilation process, places the XAML as an internal resource within the EXE or DLL. In the constructor of a XAML root element type (such as MainPage), a call is made to InitializeComponent. This method uses a static helper method Application::LoadComponent to load the XAML and parse it—creating objects, setting properties, and so on. Here’s the implementation created by the compiler for InitializeComponent (in MainPage.g.hpp, with some code cleaning):

void MainPage::InitializeComponent() {
if (_contentLoaded)
return;
_contentLoaded = true;
// Call LoadComponent on ms-appx:///MainPage.xaml
Application::LoadComponent(this,
ref new ::Windows::Foundation::Uri(
L"ms-appx:///MainPage.xaml"),
ComponentResourceLocation::Application);
}

Connecting XAML, H, and CPP files to the build process

From a developer’s perspective, working with a XAML file carries with it two other files, the H and CPP. Let’s examine them in a little more detail. Here’s the default MainPage.xaml.h (comments and namespaces removed):

#include "MainPage.g.h"
namespace BasicXaml {
public ref class MainPage sealed {
public:
MainPage();
protected:
virtual void OnNavigatedTo(NavigationEventArgs^ e)
override;
};
}

The code shows a constructor and a virtual method override named OnNavigatedTo (unimportant for this discussion). One thing that seems to be missing is the InitializeComponent method declaration mentioned in the previous section. Also the inheritance from Page that was hinted at earlier is missing. It turns out that the XAML compiler generates another header file named MainPage.g.h (g stands for generated) based on the XAML itself (this is evident with the top #include declaration). This file contains the following (it can be opened easily by selecting the Project | Show All Files, or the equivalent toolbar button, or right clicking on #include and selecting Open Document…):

namespace BasicXaml {
partial ref class MainPage : public Page,
public IComponentConnector {
public:
void InitializeComponent();
virtual void Connect(int connectionId, Object^ target);
private:
bool _contentLoaded;
};
}

Here we find the missing pieces. Here we find InitializeComponent, as well as the derivation from Page. How can there be more than one header file per class? A new C++/CX feature called partial classes allows this. The MainPage class is marked as partial, meaning there are more parts to it. The last part should not be marked as partial, and should include at least one header so that a chain forms, eventually including all the partial headers; all these headers must be part of the same compilation unit (a CPP file). This MainPage.g.h file is generated before any compilation happens; it’s generated on the fly while editing the XAML file. This is important because named elements are declared in that file, providing instance intellisense.

During the compilation process, MainPage.cpp is finally compiled, producing an object file, MainPage.obj. It still has some unresolved functions, such as InitializeComponent. At this time, MainPage.obj (along with other XAML object files, if exist) is used to generate the metadata (.winmd) file.

To complete the build process, the compiler generates MainPage.g.hpp, which is actually an implementation file, created based on the information extracted from the metadata file (the InitializeComponentimplementation is generated in this file). This generated file is included just once in a file called XamlTypeInfo.g.cpp, which is also generated automatically based on the metadata file, but that’s good enough so that MainPage.g.hppis finally compiled, allowing linking to occur correctly.

Resources

The term “resources” is highly overloaded. In classic Win32 programming, resources refer to read-only chunks of data, used by an application. Typical Win32 resources are strings, bitmaps, menus, toolbars, and dialogs, but custom resources can be created as well, making Win32 treat those as unknown chunks of binary data.

WinRT defines binary, string, and logical resources. The following sections discuss binary and logical resources (string resources are useful for localization scenarios and will not be discussed in this section).

Binary resources

Binary resources refer to chunks of data, provided as part of the application’s package. These typically include images, fonts, and any other static data needed for the application to function correctly.

Binary resources can be added to a project by right-clicking on the project in Solution Explorer, and selecting Add Existing Item. Then, select a file that must be in the project’s directory or in a subdirectory.

Contrary to C# or VB projects, adding an existing item from a location does not copy the file to the project’s directory. This inconsistency is a bit annoying for those familiar with C#/VB projects, and hopefully will be reconciled in a future Visual Studio version or service pack.

A typical Store app project already has some binary resources stored in the Assets project folder, namely images used by the application:

Using folders is a good way to organize resources by type or usage. Right-clicking on the project node and selecting Add New Filter creates a logical folder, to which items may be dragged.

Again, contrary to C#/VB projects, project folders are not created in the filesystem. It’s recommended that these are actually created in the filesystem for better organization.

The added binary resource is packaged as part of the application’s package and is available in the executable folder or subfolder, keeping its relative location. Rightclicking on such a resource and selecting Properties yields the following dialog:

The Content attribute must be set to Yes for the resource to be actually available (the default). Item Type is typically recognized by Visual Studio automatically. In case, it doesn’t, we can always set it to Text and do whatever we want with it in code.

Don’t set Item Type to Resource. This is unsupported in WinRT and will cause compile errors (this setting is really for WPF/Silverlight).

Binary resources can be accessed in XAML or in code, depending on the need. Here’s an example of using an image named apple.png stored in a subfolder in the application named Images under the Assets folder by an Image element:

<Image Source="/Assets/Images/apple.png" />

Note the relative URI. The preceding markup works because of a type converter that’s used or the Image::Source property (which is of the type ImageSource). That path is really a shortcut for the following, equivalent, URI:

<Image Source="ms-appx:///Assets/Images/apple.png" />

Other properties may require a slightly different syntax, but all originate through the ms-appx scheme, indicating the root of the application’s package.

Binary resources that are stored in another component referenced by the application can be accessed with the following syntax:

<Image Source="/ResourceLibrary/jellyfish.jpg" />

The markup assumes that a component DLL named ResourceLibrary.Dll is referenced by the application and that a binary resource named jellyfish.jpg is present in its root folder.

Logical resources

Binary resources are not new or unique to Store apps. They’ve been around practically forever. Logical resources, on the other hand, is a more recent addition. First created and used by WPF, followed by the various versions of Silverlight, they are used in WinRT as well. So, what are they?

Logical resources can be almost anything. These are objects, not binary chunks of data. They are stored in the ResourceDictionary objects, and can be easily accessed in XAML by using the StaticResource markup extension.

Here’s an example of two elements that use an identical brush:

<Ellipse Grid.Row="0" Grid.Column="1">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Offset="0" Color="Green" />
<GradientStop Offset=".5" Color="Orange" />
<GradientStop Offset="1" Color="DarkRed" />
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Rectangle Grid.Row="1" Grid.Column="1" StrokeThickness="20">
<Rectangle.Stroke>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Offset="0" Color="Green" />
<GradientStop Offset=".5" Color="Orange" />
<GradientStop Offset="1" Color="DarkRed" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>

The problem should be self-evident. We’re using the same brush twice. This is bad for two reasons:

  • If we want to change the brush, we need to do it twice (because of the duplication). Naturally, this is more severe if that brush is used by more than two elements.

  • Two different objects are created, although just one shared object is needed.

LinearGradientBrush can be turned into a logical resource (or simply a resource) and referenced by any element that requires it. To do that, the brush must be placed in a ResourceDictionary object. Fortunately, every element has a Resources property (of type ResourceDictionary) that we can use. This is typically done on the root XAML element (typically a Page), or (as we’ll see in a moment) in the application’s XAML (App.Xaml):

<Page.Resources>
<LinearGradientBrush x_Key="brush1" EndPoint="0,1">
<GradientStop Offset="0" Color="Green" />
<GradientStop Offset=".5" Color="Orange" />
<GradientStop Offset="1" Color="DarkRed" />
</LinearGradientBrush>
</Page.Resources>

Any logical resource must have a key, because it’s in a dictionary. That key is specified by the x:Key XAML directive. Once placed, a resource can be accessed from any element within that Page with the StaticResource markup extension as follows:

<Ellipse Fill="{StaticResource brush1}" />
<Rectangle Stroke="{StaticResource brush1}" StrokeThickness="40" />

The StaticResource markup extension searches for a resource with the specified key starting from the current element. If not found, the search continues on the resources with its parent element (say a Grid). If found, the resource is selected (it is created the first time it’s requested), and StaticResource is done. If not found, the parent’s parent is searched and so on. If the resource is not found at the top level element (typically a Page, but can be a UserControl or something else), the search continues in the application resources (App.xaml). If not found, an exception is thrown. The search process can be summarized by the following diagram:

Why is the markup extension called StaticResource? Is there a DynamicResource? DynamicResource exists in WPF only, which allows a resource to be replaced dynamically, with all those bound to it noticing the change. This is currently unsupported by WinRT.

There is no single call that is equivalent to StaticResource, although it’s not difficult to create one if needed. The FrameworkElement::Resources property can be consulted on any required level, navigating to the parent element using the Parent property. The Application::Resources property has special significance, since any resources defined within it can be referenced by any page or element across the entire application. This is typically used to set various defaults for a consistent look and feel.

It may be tempting to store actual elements as resources (such as buttons). This should be avoided because resources are singletons within their usage container; this means referencing that button more than once within the same page will cause an exception to be thrown on the second reference, because an element can be in the visual tree just once.

Resources are really intended for sharable objects, such as brushes, animations, styles, and templates.

Resources can be added dynamically by using the ResourceDictionary::Insert method (on the relevant ResourceDictionary) and removed by calling ResourceDictionary::Remove. This only has an effect on subsequent {StaticResource} invocations; already bound resources are unaffected.

A StaticResource markup extension can be used by a resource as well. For this to work, any StaticResource must reference a resource that was defined earlier in the XAML; this is due to the way the XAML parser works. It cannot find resources that it has not yet encountered.

Managing logical resources

Logical resources may be of various types, such as brushes, geometries, styles, templates, and more. Placing all those resources in a single file, such as App.xaml, hinders maintainability. A better approach would be to separate resources of different types (or based on some other criteria) from their own files. Still, they must be referenced somehow from within a common file such as App.xaml so that they are recognized.

ResourceDictionary can incorporate other resource dictionaries using its MergedDictionaries property (a collection). This means a ResourceDictionary can reference as many resource dictionaries as desired and can have its own resources as well. The Source property must point to the location of ResourceDictionary. The default App.xaml created by Visual Studio contains the following (comments removed):

<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="Common/StandardStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

Indeed, we find a file called StandardStyles.xaml in the Common folder, which hosts a bunch of logical resources, with ResourceDictionary as its root element. For this file to be considered when StaticResource is invoked, it must be referenced by another ResourceDictionary, from a Page or the application (the application is more common). The ResourceDictionary::MergedDictionaries property contains other ResourceDictionary objects, whose Source property must point to the required XAML to be included (that XAML must have ResourceDictionary as its root element).

We can create our own ResourceDictionary XAML by using Visual Studio’s Add New Item menu option and selecting Resource Dictionary:

Duplicate keys

No two objects can have the same key in the same ResourceDictionary instance. StaticResource takes the first resource it finds with the specified key, even if that key already exists in a ResourceDictionary. What about merged dictionaries?

Merging different resource dictionaries may cause an issue—two or more resources with the same key that originate from different merged dictionaries. This is not an error and does not throw an exception. Instead, the selected object is the one from the last resource dictionary added (which has a resource with that key). Furthermore, if a resource in the current resource dictionary has the same key as any of the resources in its merged dictionaries, it always wins out. Here’s an example:

<ResourceDictionary>
<SolidColorBrush Color="Blue" x_Key="brush1" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Brushes2.xaml" />
<ResourceDictionary Source="Resources/Brushes1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Given this markup, the resource named brush1 is a blue SolidColorBrush because it appears in the ResourceDictionary itself. This overrides any resources named brush1 in the merged dictionaries. If this blue brush did not exist, brush1 would be looked up in Brushes1.xaml first, as this is the last entry in the merged dictionaries collection.

XAML containing a ResourceDictionary as its root can be loaded dynamically from a string using the static XamlReader::Load method and then added as a merged dictionary, where appropriate.

Styles

Consistency in user interface is an important trait; there are many facets of consistency, one of which is the consistent look and feel of controls. For example, all buttons should look roughly the same—similar colors, fonts, sizes, and so on. Styles provide a convenient way of grouping a set of properties under a single object, and then selectively (or automatically, as we’ll see later) apply it to elements.

Styles are always defined as resources (usually at the application level, but can also be at the Page or UserControl level). Once defined, they can be applied to elements by setting the FrameworkElement::Style property.

Here’s a style defined as part of the Resources section of a Page:

<Page.Resources>
<Style TargetType="Button" x_Key="style1">
<Setter Property="FontSize" Value="40" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush >
<GradientStop Offset="0" Color="Yellow" />
<GradientStop Offset="1" Color="Orange" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="DarkBlue" />
</Style>
</Page.Resources>

The style has a key (style1), and must have TargetType. This is the type the style may be applied to (and any derived types). The XAML parser has a type converter that converts TargetType to a TypeName object.

The main ingredient in Style is its Setters collection (which is also its ContentProperty). This collection accepts Setter objects, which need Property and Value. The property must be a dependency property (not usually a problem, as most element properties are dependency properties); these are provided as simple strings thanks to type converters used behind the scene.

The above markup sets up the properties FontSize, Background (with a complex property syntax because of the LinearGradientBrush), and Foreground—all for the Button controls.

Once defined, the style can be applied to elements using the usual StaticResource markup extension in XAML by setting the FrameworkElement::Style property, as in the following example:

<Button Content="Styled button" Style="{StaticResource style1}" />

Readers familiar with WPF may be wondering if the TargetType property can be omitted so that a greater control range can be covered. This is unsupported in the current version of WinRT.

Setting the style on an incompatible element type (such as a CheckBox control in this example) causes an exception to be thrown at page load time. If a CheckBox should also be able to use the same style, the TargetType can be changed to ButtonBase (which covers all button types).

Use different styles for different elements, even if a base type seems to cover several controls. It’s very likely that later some properties may need to be tweaked for a particular type, making it difficult to change the style. Build a different style for different concrete types. You can also use style inheritance (as described later) to shorten some of the markup.

What happens if an element with an applied style sets a property to a different value than the one from Style? The local value wins out. This means that the following button has a font size of 30 and not 40:

<Button Content="Styled button" FontSize="30"
Style="{StaticResource style1}" />

Implicit (automatic) styles

The previous section showed how to create a style that has a name (x:Key) and how to apply it to elements. Sometimes, however, we would like a style to be applied automatically to all elements of a certain type, to give the application a consistent look. For example, we may want all buttons to have a certain font size or background, without the need for setting the Style property of each and every button. This makes creating new buttons easier, as the developer/designer doesn’t have to know what style to apply (if any, the implicit style in scope will be used automatically).

To create a Style that is applied automatically, the x:Key attribute must be removed:

<Style TargetType="Button">

</Style>

The key still exists, as the Style property is still part of ResourceDictionary (which implements IMap<Object, Object>), but is automatically set to a TypeName object for the specified TargetType.

Once the Style property is defined and any Button element (in this example) in scope for ResourceDictionary of the Style property is in, that style will be applied automatically. The element can still override any property it wishes by setting a local value.

Automatic styles are applied to the exact type only, not to derived types. This means that an automatic style for ButtonBase is useless, as it’s an abstract class.

An element may wish to revert to its default style and not have an implicit style applied automatically. This can be achieved by setting FrameworkElement::Style to nullptr (x:Null in XAML).

Style inheritance

Styles support the notion of inheritance, somewhat similar to the same concept in object orientation. This is done using the BasedOn property that must point to another style to inherit from. The TargetType of the derived style must be the same as in the base style.

An inherited style can add Setter objects for new properties to set, or it can provide a different value for a property that was set by the base style. Here’s an example for a base style of a button:

<Style TargetType="Button" x_Key="buttonBaseStyle">
<Setter Property="FontSize" Value="70" />
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="40,10" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>

The following markup creates three inherited styles:

<Style TargetType="Button" x_Key="numericStyle"
BasedOn="{StaticResource buttonBaseStyle}">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style TargetType="Button" x_Key="operatorStyle"
BasedOn="{StaticResource buttonBaseStyle}">
<Setter Property="Background" Value="Orange" />
<Setter Property="Foreground" Value="Black" />
</Style>
<Style TargetType="Button" x_Key="specialStyle"
BasedOn="{StaticResource buttonBaseStyle}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</Style>

These styles are part of a simple integer calculator application. The calculator looks like this when running:

Most of the elements comprising the calculator are buttons. Here is the numeric button markup:

<Button Style="{StaticResource numericStyle}" Grid.Row="1"
Content="7" Click="OnNumericClick" />
<Button Style="{StaticResource numericStyle}" Grid.Row="1"
Grid.Column="1" Content="8" Click="OnNumericClick"/>
<Button Style="{StaticResource numericStyle}" Grid.Row="1"
Grid.Column="2" Content="9" Click="OnNumericClick"/>

The operator buttons simply use a different style:

<Button Style="{StaticResource operatorStyle}" Grid.Row="3"
Grid.Column="3" Content="-" Click="OnOperatorClick"/>
<Button Style="{StaticResource operatorStyle}" Grid.Row="4"
Grid.Column="3" Content="+" Grid.ColumnSpan="2"
Click="OnOperatorClick"/>

The = button uses the same style as operators, but changes its background by setting a local value:

<Button Style="{StaticResource operatorStyle}" Grid.Row="4"
Grid.Column="1" Grid.ColumnSpan="2" Content="="
Background="Green" Click="OnCalculate"/>

The complete project is named StyledCalculator and can be found as part of the downloadable source for this article.

Style inheritance may seem very useful, but should be used with caution. It suffers from the same issues as object oriented inheritance in a deep inheritance hierarchy—a change in a base style up in the hierarchy can affect a lot of styles, being somewhat unpredictable, leading to a maintenance nightmare. Thus, a good rule of thumb to use is to have no more than two inheritance levels. Any more than that may cause things to get out of hand.

Store application styles

A Store app project created by Visual Studio has a default style file named StandardStyles.xaml in the Common folder. The file includes styles for all common elements and controls the set up for a common look and feel that is recommended as a starting point. It’s certainly possible to change these styles or to inherit from them if needed.

WinRT styles are similar in concept to CSS used in web development to provide styling to HTML pages. The cascading part hints to the multilevel nature of CSS, much like the multilevel nature of WinRT styles (application, page, panel, specific element, and so on).

Summary

This article was all about XAML, the declarative language used to build user interfaces for Windows Store apps. XAML takes some time getting used to it, but its declarative nature and markup extensions cannot easily be matched by procedural code in C++ (or other languages). Designer-oriented tools, such as Expression Blend and even the Visual Studio designer make it relatively easy to manipulate XAML without actually writing XAML, but as developers and designers working with other XAML-based technologies have already realized, it’s sometimes necessary to write XAML by hand, making it an important skill to acquire.

Resources for Article :


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here