Wednesday, April 19, 2023

global (and implicit) using directives - but for XAML?

One of the great things about .NET 6 and C# 10 was the introduction of global and implicit using directives. Namespaces that could be defined once and then made available to all C# files in a project.

These small features simplified code by reducing the boilerplate and noise at the top of each file.

As part of my exploration into how XAML files could be made better, I've been looking at whether something similar could be done and whether the duplication at the top of each file could be removed.

The top of a XAML file with all the namespace aliases highlighted
The five highlighted lines in the above image (and many like them) are in almost every file. I even have some files with many more aliases defined. 

These lines add noise and distraction. Plus, inconsistent use of XML namespace aliases across different files can become confusing and lead to errors and unexpected behavior. 

What if we could have all the namespaces (and aliases) defined in a single place and available to all xaml files in a project? With a file like this:

File showing just the xmlns statements from the first image

I decided to see if I could make this possible.

Because I have previously spent time looking at the internal .NET MAUI code relating to XAML files, I started there.

In theory, this idea is applicable to any flavor of XAML, but I wanted to start by creating a working example of what I was thinking as after having talked to hundreds of developers about thinking of XAML in different ways, I know that many have a hard time even considering something different to what they've always done. I hoped that showing what was (or could be) possible might help open imaginations more easily.

But it's not valid XML!

Before I started, I knew that there would be objections to this idea. 

I knew some would not like it because it is different.

I know some would not like it because it technically makes the document invalid. XAML documents are (or should be) valid XML documents and, as such, cannot use undeclared namespaces (and aliases).

I pressed on regardless. I knew it will make the document technically invalid, but I hoped that the benefits it might bring could outweigh this.
If going back to the C# comparison, a C# file that relies on namespace directives that are implicit or defined (globally) in another file isn't much good on its own, either. Similarly, a XAML file that is "valid" but makes references to types that are in other files is of limited use by itself too.
This wasn't enough to stop me.

Going through the XAML source, I soon discovered that XAML files are potentially read (and things done with them) in many different places in the code and in different ways. The small chance that this would be an easy thing to explore and experiment with quickly got smaller and dissolved away entirely.

But I pressed on.

My goal of showing a working example was quickly scaled back to be just the smallest proof of concept appropriate.

Rather than support multiple namespace aliases, I opted for just one. (xmlns:x="")

And I got there. 

Look, here's a file without the "x" alias defined, but it still works.

A small XAML document without the "x" alias defined but still used

I know you're skeptical.

After all, look at those blue squiggly lines under the code where the "x" alias is used.

It turns out they're coming from the editor. It's trying to parse the document too.

As soon as I saw how frequently the MAUI code processed XAML content as XML, I thought this could be a problem. I had an increased concern that there could be a lot of other tools and features that relied on XAML files always being valid XML documents.

As a simple example, here's the dialog I see every time I save that file.

Information dialog: --------------------------- Microsoft Visual Studio --------------------------- Error in StylerPackage:  'x' is an undeclared prefix. Line 3, position 5.    If this deems a malfunctioning of styler, please kindly submit an issue at --------------------------- OK    ---------------------------

Very early on in my thinking about this, I did worry that additional tools needing to be able to process XAML files could be an issue. It quickly became a bigger issue than I expected.

I've looked at lots of code that works with XAML files (and written a lot, too), and the majority of it does treat XAML as XML and uses framework-provided functionality.
In fact, to get MAUI to treat my modified XAML documents as valid XML, I ended up having to add the missing namespace alias declarations into the document each time it tried to process them.

This wouldn't have been the end of the world if such a modification was needed in one or two places, but this isn't practical on a such a wide level. There is too much code (inside VS, frameworks, extensions, and other tools) that assumes XAML files will always be valid XML files too.

[Aside: I assume C# was able to add features such as global and implicit namespace directives as it is responsible for the entire toolchain that reads, parses, and complies '.cs' documents. Any 3rd party code that isn't using Roslyn/CodeAnalysis libraries to do the work has to take care of any special scenarios like this by itself, but I expect very few do. It's not like XAML which has been around for a long time and always supported files being treated as valid XML.]

So, my experiment was a failure.

In part.

I'd learned that this wasn't a practical thing to implement and that the impact on the wider ecosystem was almost certainly going to be too big for me to overcome.

Like all good experiments, I wasn't trying to do this. I was testing a hypothesis that it was possible and that it would be useful if it was.

I'd hoped to get to the point of starting a wider discussion on making global XML namespace aliases possible in XAML by showing that it was achievable. A discussion on how best to implement it while providing something that others could experiment with so they could experience the benefits is not to be.

But, as part of this experiment, I've become increasingly pleased by not having to look at all the noise the alias declarations add to the opening element of a XAML file.

Rather than remove them, what if they were hidden?

Well, this feels like a simpler thing to implement and with a much lower risk of side effects too.

Take a look at the following highlighted item. (It's in a WinUI3 project, but that doesn't matter.) 

A partial XAML file screenshot showing a new outlining node next to xmlns multi-line attributes

Yes, it's a new outlining node.

Guess what it can be used to collapse... or look below.

As with the above but all the lines containing xmlns declaraions ahve been collapsed behind a comment reading "[xmlns...]"

Isn't that nicer?

And for completeness, here's the original document from above with the 5 long lines all now collapsed behind one short placeholder. 

Partial XAML screenshot with 5 xmlns attributes hidden

I'll make this publicly available soon.

Of course, this doesn't address the duplication across files and the possibility of issues due to aliases being used inconsistently across files within a project, but I have separate ideas for addressing those.

But for that, and details of other ways to XAML can be improved (without needing to make changes to the frameworks being used,) stay tuned for future posts.


Post a Comment

I get a lot of comment spam :( - moderation may take a while.