#103 – XAML 2009

.NET 4.0 introduced an update to the supported XAML vocabulary–the latest version supported is now XAML 2009.  WPF and Silverlight do not yet support XAML 2009 (.NET 4 / Visual Studio 2010), but still support XAML 2006.

With respect to Visual Studio 2010, therefore, the features introduced in XAML 2009 can only be used in loose XAML files.

XAML 2009 introduces the following new features, beyond what was present in XAML 2006:

  • x:Arguments allows calling non-default constructor (one with parameters)
  • x:FactoryMethod allows calling a static method to construct an object rather than using a constructor
  • x:Reference markup extension makes it easier to set a property value to point to an instance of another object
  • x:TypeArguments allows use of generics
  • Built-in support in x: namespace for standard CLR primitive data types (e.g. string, int, float, etc).  Avoids adding a separate XML namespace.

See also: XAML 2009 Language Features

Advertisements

#102 – Using XamlReader to Load a Loose XAML File

When you use Visual Studio to create the main Window, App and Page XAML files that make up a WPF application, code is automatically generated to cause the XAML (BAML) files to be read at runtime.

You can also use the XamlReader class to read loose XAML files–ones that have not be converted to BAML and stored as a resource in your application.

The static XamlReader.Load method will read a XAML file, instantiate all objects defined in the file, and return a reference to the top-level (root) element from the file.  (XamlReader is in the System.Windows.Markup namespace).

Assuming that you have a XAML filed named Stuff.xaml and it has a root element that is a StackPanel, you could load the file as follows:

 StackPanel sp1;
 using (FileStream fs = new FileStream("Stuff.xaml", FileMode.Open))
 {
     sp1 = (StackPanel)XamlReader.Load(fs);
 }

#101 – What Visual Studio Does with Your XAML

Given that XAML is just a declarative representation of the objects used by your WPF application, the objects need to be instantiated at runtime.

Here’s the process (e.g. for MainWindow.xaml):

  • While you work in designer, VS2010 automatically generates partial class (e.g. MainWindow.g.i.cs), including:
    • Reference variables for named elements in XAML  (e.g. myButton)
    • Code to instantiate XAML objects at runtime
  • When you build the project
    • All code (yours and VS2010-generated) is compiled
    • XAML is compiled into BAML (binary), stored in .baml file
    • All BAML files combined into single .g.resources file (e.g. MyApp.g.resources)
    • Executable is built, embedding the .g.resources file as a resource
  • At runtime
    • Window constructor calls InitializeComponent
    • InitializeComponent (in generated code) calls Application.LoadComponent, passing URI identifying XAML
    • LoadComponent loads binary XAML from embedded resource, creates all objects
    • As BAML is read, window’s IComponentConnector.Connect method is called, which hooks up local object references to the created objects and attaches event handlers

#100 – Loose XAML Files

A loose XAML file is a file containing XAML that has no associated code-behind and is not compiled into an application.  A loose XAML file can be loaded into Internet Explorer, where the user interface will be rendered.

Some rules about loose XAML files:

  • The top-level element cannot be a Window, but could be a Page or other GUI element (e.g. StackPanel).
  • The top-level element must have any required namespaces defined
  • The top-level element cannot have a x:Class attribute
  • No elements in the file can have event handlers
  • The only browser supported is Internet Explorer (Firefox can render loose XAML with WPF plug-in)

#99 – Creating Custom Objects in XAML

In XAML, you normally create elements that come from the standard WPF and XAML namespaces.  But you can also create objects that are instances of your own custom classes, or standard .NET classes in other namespaces.

You start by including a new namespace attribute, to indicate the namespace and assembly where your class can be found.  In the following example, we set up the m: prefix as pointing to the namespace MyStuff, which is in the assembly MyStuffLib.dll.

 xmlns:m="clr-namespace:MyStuff;assembly=MyStuffLib"

Now we can instantiate objects from the MyStuff namespace.  Assuming that MyStuff includes a Movie class, we could create an instance of a Movie in a window’s resource dictionary:

 <Window.Resources>
     <m:Movie x:Key="movie1" Title="Dances With Wolves" Director="Kevin Costner"/>
 </Window.Resources>

One reason for doing this would be so that we could then use data binding to bind one or more controls to the custom object.

#98 – How Attached Properties Work in WPF

Attached properties in XAML allow attaching a property value to an object that is of a different type than the type where the property is defined.  (E.g. Grid.Row property can be set for a Button).

When the XAML parser encounters an attached property, it calls a static method on the class where the property is defined.  So setting a value for Grid.Row on a Button is equivalent to:

 Grid.SetRow(myButton, 1);

In WPF, attached properties are often implemented using dependency properties.  The object on which the property is being set (e.g. Button) inherits from DependencyObject, which means that it can store a collection of dependency properties.

When dependency properties are used for storing the values of attached properties, the static setter method in turn just calls the SetValue method on the object passed in to it.  So the method listed above would be implemented as:

 myButton.SetValue(Grid.RowProperty, 1);

#97 – Attached Properties

In XAML, attribute syntax is normally used to set the value of a property on an object.  But through the use of attached properties, you can set a value for a property that comes from another object.

Attached property syntax follows the form TypeName.PropertyName, where the type named is the type where the property is defined.

In the example below, we have two Button controls contained in a grid.  To indicate their position in the grid, we specify Grid.Row and Grid.Column properties for each.  The buttons don’t have Row and Column properties and neither does the Grid .  But the Grid does have Row and Column as attached properties–meaning that it can apply the properties to other objects, indicating where those objects should appear in the grid.

 <Grid>
     <Button Grid.Row="1" Grid.Column="0" Content="Dee" Height="23" Name="button1" Width="75" />
     <Button Grid.Row="1" Grid.Column="1" Content="Dum" Height="23" Name="button2" Width="75" />
 </Grid>