UI Code Expert

Exploring XAML Markup Extensions

Learn about the often-overlooked XAML extensibility resource, and how you can make custom markup extensions of your own.

Early on in a developer's XAML programming experience, he'll encounter the ability to assign values to properties using either XAML object element syntax or XAML attribute syntax. The following code demonstrates each approach using a System.Windows.Controls.Text-Box (the attribute usage syntax appears first):

<!-- Attribute Usage -->
<TextBox HorizontalAlignment="Left" VerticalAlignment="Top" 
          Name="AttributeUsageTextBox" 
          Text="Hello! My name is Inigo Montoya." />

<!-- Element Usage -->
<TextBox >
  <TextBox.HorizontalAlignment>Left</TextBox.HorizontalAlignment>
  <TextBox.VerticalAlignment>Top</TextBox.VerticalAlignment>
  <TextBox.Margin>0,28,0,0</TextBox.Margin>
  <TextBox.Name>ObjectElementUsageSyntaxTextBox</TextBox.Name>
  <TextBox.Text>You killed my father, prepare to die</TextBox.Text>
</TextBox>

On occasion, however, the attribute syntax is combined with curly braces ({}), and the value specified is interpreted with special syntax, rather than as a literal string, as shown here:

<TextBox HorizontalAlignment="Left" VerticalAlignment="Top" 
          Name="AttributeUsageTextBox" 
          Text="{x:Static system:Environment.UserName}" />

In this case, rather than providing hardcoded text, we assign the text to a static property, System.Environment.UserName.

Parenthetically, we could use object element syntax to specify the user name. I show this approach in the following code (note that this syntax is noticeably more verbose):

<!-- Element Usage -->
<TextBox >
  <TextBox.HorizontalAlignment>Left</TextBox.HorizontalAlignment>
  <TextBox.VerticalAlignment>Top</TextBox.VerticalAlignment>
  <TextBox.Name>ObjectElementUsageSyntaxTextBox</TextBox.Name>
  <TextBox.Text>
    <x:Static Member="system:Environment.UserName" />
  </TextBox.Text>
</TextBox>

A second parenthetical note when using XAML attribute syntax: If you explicitly want curly braces, you can have them for the entire string by prefixing with empty curly braces (for instance, {}{Stop that rhyming}), or by prefixing each curly brace with a backslash.

The key thing about the attribute syntax is that it reveals the use of an extensibility mechanism within XAML: the markup extension. x:Static identifies a class within the winfx namespace called System.Windows.Markup.StaticExtension. In fact, the full name, "StaticExtension," could be used instead of the abbreviated name, "Static," as illustrated here:

<TextBox HorizontalAlignment="Left" VerticalAlignment="Top" 
          Name="AttributeUsageTextBox" 
          Text="{x:StaticExtension system:Environment.UserName}" />

This is similar to the way that the suffix of "Attribute" can be dropped from a .NET attribute's class name when it's used to decorate a construct within C# or Visual Basic .NET. As an example, consider [Serializable]class Person{} rather than [SerializableAttribute]class Person{}. (Note that the optional use of the "Extension" suffix is not available in Silverlight.)

There are other ways that the XAML markup extension syntax mirrors how .NET attributes are used in C# and VB.NET. For example, notice that, when using the Static extension with the previous XAML element syntax, we needed to specify the value of "Member," whereas in the XAML attribute syntax this was implied.

This was possible because System.Windows.Markup.Static-Ex-tension contains a constructor that takes a single string parameter called "member," and thereby enables a positional means of providing values for a StaticExtension instance. The constructor is declared as public StaticExtension(string member), implying that the first (and only) parameter on the constructor will set the Member property on a StaticExtension instance. In the XAML object element syntax, no positional parameter was provided, so StaticExtension's default constructor is called and a named argument ("Member") explicitly assigned the Member property on the StaticExtension instance.

Here are some additional examples of markup extensions.

First, I show specifying a resource element from a resource dictionary using the StaticResource markup extension (System.Windows.StaticResourceExtension):

<StackPanel>
  <StackPanel.Resources>
    <ResourceDictionary>
      <system:String x:Key="TextBox.Text">
        Hello! My name is Inigo Montoya.</system:String>
    </ResourceDictionary>
  </StackPanel.Resources>
  <TextBox HorizontalAlignment="Left" VerticalAlignment="Top" 
            Name="TextBox" 
            Text="{StaticResource ResourceKey=TextBox.Text}" />
</StackPanel>

Next, setting a property to null using the Null markup extension (System.Windows.Markup.NullExtension):

<StackPanel>
  <StackPanel.Resources>
   <Style x:Key="DefaultTextBox"  
      TargetType="{x:Type TextBox}">
      <Setter Property="Control.Background" Value="Red"/>
    </Style>  
  </StackPanel.Resources>
  <!-- Attribute Usage -->
  <TextBox HorizontalAlignment="Left" VerticalAlignment="Top" 
            Name="TextBox" 
            Text="{x:StaticExtension system:Environment.UserName}"
            Background="{x:Null}" 
            Style="{StaticResource DefaultTextBox}"/>
</StackPanel>

In the previous example, the Background is set to red by the style, but we override that style by explicitly setting it to null using the Null markup extension.

Then there's identifying a type within XAML using the Type markup extension (System.Windows.Markup.TypeExtension). This occurs in the previous example with:

TargetType="{x:Type TextBox}">

Finally, there's creating a collection of values within XAML using the Array markup extension (System.Windows.Markup.Array-Ex-tension), as shown here:

<ListBox>
  <ListBox.ItemsSource>
    <x:Array Type="{x:Type system:String}">
      <system:String>Doc</system:String>
      <system:String>Happy</system:String>
      <system:String>Bashful</system:String>
    </x:Array>
  </ListBox.ItemsSource>
</ListBox>

In this example we use the XAML object element syntax because the ArrayExtension doesn't support XAML attribute syntax, as the elements of the array need to be specified.

Although not shown in the preceding example, one of the most common markup extensions is System.Windows.Data.Binding (which doesn't happen to follow the convention of ending with the "Extension" suffix). We'll save further discussion of Binding for a future UI column, except to point out that XAML with the Binding markup extension frequently uses nested markup extensions. Nested markup extensions are specified by nesting the curly braces within each other, as in:

Content="{Binding Path="Name", RelativeSource="{RelativeSource Self}}" 

As pointed out earlier, markup extensions are an extensibility mechanism within XAML. Therefore, it should be no surprise that it's possible to create custom markup extensions of your own. Simply declare a class that derives from System.Windows.Markup.Markup-Ex-tension and then implement its ProvideValue abstract member. Here's some sample code:

public class UniqueIdExtension : System.Windows.Markup.MarkupExtension
{
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    return Guid.NewGuid();
  }
}

As with StaticResource, adding a constructor with parameters (in addition to the default constructor) will add support for passing arguments via position, and adding public properties will enable support for named arguments.

Most programmers have learned XAML by example. However, it's not uncommon that they're unaware of markup extensions and the details of how they work. In this article, we were able to highlight these details to not only clarify their use, but also to explain how to create your own custom markup extensions.

About the Author

Mark Michaelis (http://IntelliTect.com/Mark) is the founder of IntelliTect and serves as the Chief Technical Architect and Trainer. Since 1996, he has been a Microsoft MVP for C#, Visual Studio Team System, and the Windows SDK and in 2007 he was recognized as a Microsoft Regional Director. He also serves on several Microsoft software design review teams, including C#, the Connected Systems Division, and VSTS. Mark speaks at developer conferences and has written numerous articles and books - Essential C# 5.0 is his most recent. Mark holds a Bachelor of Arts in Philosophy from the University of Illinois and a Masters in Computer Science from the Illinois Institute of Technology. When not bonding with his computer, Mark is busy with his family or training for another triathlon (having completed the Ironman in 2008). Mark lives in Spokane, Washington, with his wife Elisabeth and three children, Benjamin, Hanna and Abigail.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube