Alex's blog

XAML static sites

Athena Lilith Martin

Published on

So... I must apologize for this concept in advance. I've thought, in some fediverse posts, about the concept of using Microsoft's XAML as the basis for a static site generator. This is an XML-based language Microsoft uses for declarative UI in .NET, among other things. Again, let me preface this all with an apology and a clear statement that this isn't really a serious idea, just the result of me getting too much of a concentrated dose of .NET at work.

What is XAML?

Before we start talking about things we could do with XAML, we need to understand what the darn thing actually even is.

XAML is, at its core, a way of writing boilerplate initializer code. For example, instead of writing this C# code:

public SomePageSubclass() {
  Title = "Main page";
  MinWidth = 200;
  MinHeight = 100;
  
  StackPanel panel = new();
  Content = panel;
  
  Label header = new();
  header.Content = "Welcome";
  header.FontSize = 16;
  header.FontStyle = FontStyle.Bold;
  panel.Children.Add(header);
  
  StackPanel buttonBar = new();
  buttonBar.Orientation = Orientation.Horizontal;
  panel.Children.Add(buttonBar);
  
  Button firstBtn = new();
  firstBtn.Content = "First thing";
  firstBtn.Click += Click1;
  buttonBar.Children.Add(firstBtn);
  
  Button secondBtn = new();
  secondBtn.Content = "Second thing";
  secondBtn.Click += "Click2";
  buttonBar.Children.Add(secondBtn);
}

You might write XAML like this:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Namespace.SomePageSubclass" Title="Main page" MinWidth="200" MinHeight="100">
  <StackPanel>
    <Label FontSize="16" FontStyle="Bold">Welcome</Label>
    <StackPanel Orientation="Horizontal">
      <Button Click="Click1">First thing</Button>
      <Button Click="Click2">Second thing</Button>
    </StackPanel>
  </StackPanel>
</Page>

This is a little less boilerplatey, somewhat less vertical, doesn't make you give names to as many things, and shows the structure of the objects better, so it feels like an improvement to me. When you load the XAML file (or, more likely, when some infrastructure code in one of the .NET libraries based on XAML loads it or runs code compiled from it, inside an InitializeComponent() method or something), the same thing happens as when you run the C#, including wiring up the event handlers (defined in a C# partial class).

I'm going to give a rather oversimplified explanation of XAML now, so any .NET experts can skip to the next section so as not to cringe at everything I'm skipping over (dependency properties, compile-time vs. runtime, and all the other details of how this all actually works). Microsoft has lots of documentation if you want those details.

As you can probably see from looking at the example, the XML elements become objects of the classes corresponding to their names (XAML maps XML namespaces to .NET namespaces, or groups of .NET namespaces), the XML attributes set the corresponding properties, and the content of an element automatically becomes a value or member for the appropriate Content or Children property (there's an attribute to specify which one that is).

In addition to this relatively obvious behavior, properties can also be set with a more complex syntax - a <ClassName.PropertyName> element - to handle cases where a property value is a more complex object or collection of objects, and special attached properties - like Grid.Row - can be used, which are sort of tacked on by an ancestor and call code in that ancestor class.

Finally, XAML has markup extensions. These are basically function calls embedded into attribute values; using syntax like {Extension Param1=value1, Param2={AnotherExtension value2}}, properties can be set to the return value of some code probably buried deep in a library that does something like look up a resource.

But, to summarize: XAML is, at its core, an XML language for initializing object hierarchies.

The weird idea

Hey, XAML is XML, XHTML is XML, what if we glued the two together and made a static site generator that uses XAML? We could do templates like the way subclassed controls work in WPF and you could have almost seamless drop-in components.

Okay, so, putting aside that there are much easier ways to do that... How could we make the whole XAML thing work anyway?

Well, the first major hurdle is that element names have to be class names, but there's no convenient .NET library with every XML namespace ever invented coded up as classes. But, a lot of XML namespaces do have schemas, whether the W3C XML schema language (XSD) or RELAX NG or whatever. So, we can plug parsers for some of those schema languages into System.Reflection.Emit or Roslyn or something and generate the classes!

(This is a terrible idea for various reasons, but let's keep thinking about it anyway and see where we end up.)

So, for each namespace (HTML, SVG, Atom, etc.), we have a schema for it, and we run them through a compiler that spits out classes with names exactly matching the XML elements, including properties for all the attributes, and one for the children. Nice and straightforward. When we want to actually output the XML, we can recurse down through all the child elements and build up a tree. But maybe we don't want to hand-roll the XML generation, or maybe we want to do other things with the XML tree before we output it... Oh, look at that, XElement isn't sealed. All our XML element classes can subclass XElement with a zero-arg constructor that just sets the element name (making them A-OK to use in XAML), and then we can generate the attribute and children properties so that they just wrap XElement's behavior, and we can just call XElement.ToString() or XElement.Save(), maybe after a bit of template-related shuffling away of objects that aren't real XML elements we want in the output.

Once we've generated these classes, I suppose they go into a dynamic assembly and they can be regenerated at runtime each time, or maybe they get cached as a DLL for each namespace or something. Whatever, point is, there's an assembly and a CLR namespace for each XML namespace that has all the appropriate classes for the different elements in it, and they're suitable for XAML while also being XElements. So, there's not much else to do, right? Just gluing things together; map the XML namespaces to the CLR namespaces, provide some classes for templates and documents, load up all the input XAML files in a dependency-satisfying order, crawl down the hierarchy from each output root element to hoist the template content into place of the template objects, and write out the XML files. And, you know, write a bunch of hacks to work around all the Microsoft quirks I didn't think of.

So how is it?

Here's what this whole thing might look like if someone implemented it, using the example of an image lightbox component and a page that uses it.

<t:Component xmlns="http://www.w3.org/1999/xhtml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:t="clr-namespace:WwwXaml.Templates" x:Class="MyWebsite.ImgBox">
  <figure>
    <img src="{t:Binding Uri}">
      <img.alt><t:ContentPresenter /></img.alt>
    </img>
    <figcaption><t:ContentPresenter /></figcaption>
  </figure>
</Component>
<d:Page xmlns="http://www.w3.org/1999/xhtml" xmlns:d="clr-namespace:WwwXaml.Documents" xmlns:m="clr-namespace:MyWebsite" Title="Gallery">
  <main>
    <p>You can see a few photos I've taken below. These are just my favorites.</p>
    <m:ImgBox Uri="/pictures/robin.png">A robin in flight.</m:ImgBox>
    <m:ImgBox Uri="/pictures/boats.png">Boats on the lake.</m:ImgBox>
  </main>
</d:Page>

To be honest, this does actually feel somewhat pleasant. It's a little more ergonomic than my own XSite, though it's likely the internals would be a lot hairier and there are probably some unpleasant things like you have to compile the templates that would crop up in a real implementation.

Any real takeaways?

Well, mostly just that some nicer ergonomics are definitely possible for an XML-based SSG. Using the root element to specify the template is a bit friendlier than using <?xml-stylesheet?> or a custom processing instruction, if less idiomatic. Plus, being able to include templates inline smoothly by using an element in a namespace that specifies where to find it, and passing the parameters as attributes and content, is pretty nice.

And you could definitely implement all of that without using XAML.

Tagged: