Modifying XAML named field visibility

WinUI XAML WPF

6 years ago

The x:Name attribute in XAML creates named fields that you can use to access the controls from the code-behind. However, as opposed to WPF, in UWP these fields are private which means you can access them from the code-behind only, not from other classes. While noting it is a good idea from architectural standpoint, is it possible to change this behavior?

Normal behavior

Let's define a simple TextBlock control in XAML.

<TextBlock x:Name="InaccessibleTextBlock" />

Now, what happens if we create a new class that takes the page as parameter of one of the methods and tries to access the TextBlock?

public static class OutsideAccess
{
    public static void ChangeTexts(MainPage page)
    {
        page.InaccessibleTextBlock.Text = "Accessed"; //does not work!
    }
}

The app will not compile, because the field is inaccessible due to its protection level. To see what actually happens behind the scenes, let's open the auto-generated MainPage.g.i.cs file which can be found in the obj folder. We can find the following field there:

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 14.0.0.0")]
private global::Windows.UI.Xaml.Controls.TextBlock InaccessibleTextBlock;

Clearly, the field is defined as private.

x:FieldModifier directive

To change the code generation behavior, you can use the x:FieldModifier directive. This allows you to specify excatly which access modifier should be field have.

<TextBlock x:FieldModifier="public" x:Name="AccessibleTextBlock" />

Now, accessing the field from the outside works as a charm:

public static class OutsideAccess
{
    public static void ChangeTexts(MainPage page)
    {
        page.AccessibleTextBlock.Text = "Accessed";
    }
}

Note that you are not limited to public and private only, and you can also set the field to be internal or protected. We can confirm the change of visiblity was reflected in the generated source code:

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 14.0.0.0")]
public global::Windows.UI.Xaml.Controls.TextBlock AccessibleTextBlock;

WPF

If you wonder, what is the default behavior in WPF, wonder no more! WPF's convention is to set all named fields as internal by default:

[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBlock InaccessibleTextBlock;

You can use the x:FieldModifier directive to modify the visibility the same way as in UWP.

Source code

Example source code for this article is available here on my GitHub.

Summary

The x:FieldModifier is an interesting and less known directive of XAML. However, we should use it with caution as exposing fields as anything else than private is never a good idea. It is then quite surprising that in WPF internal was chosen as the default.