Search code examples
c#wpfxamlcompilationbaml

How is a graphical element dynamically created in code compiled (.NET 5)?


XAML project file is compiled into a binary representation of the XAML code - Binary Application Markup Language (BAML). Then the BAML code is embedded as a resource in the final assembly of the application - exe or dll file. It concerns XAML. However, what about the graphical elements created in C# Code (! ) while the application is running?

Let's say the application is an empty window:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<StackPanel x:Name="WindowStackPanel">
<Button Click="CreateButtonEvent" Content="I was created at compile time" />
</StackPanel>

</Window>

This XAML is compiled into an executable resource.

On mouse event, I add new button(s) - is a graphical element(s) and it was not compiled into BAML during the build of the application:

private void CreateButtonEvent(object sender, RoutedEventArgs e)
{
    WindowStackPanel.Children.Add(new Button() { Content = "Where was I created?" });
}

In this regard, the question is: what technically happens when I create a graphic item (button) in C# Code (not XAML code) while the application is running? Is it writing to the shared resource created by BAML, or is it creating its own temporary resource that JIT is already reading? Thanks


Solution

  • This is a really interesting question and I think understanding the answer really gives you a deeper understanding of how WPF (and UWP or Xamarin for that matter) work under the hood.

    Essentially, XAML has one simple function - to tell the framework how to build the visual tree. As such, anything that can be done in XAML can be translated to C#; I can't think of a single exception. Take your example:

    <StackPanel x:Name="WindowStackPanel">
         <Button Click="CreateButtonEvent" Content="I was created at compile time" />
    </StackPanel>
    

    which could be written as:

    var sp = new StackPanel() { Name = "WindowStackPanel" };
    var button = new Button() { Content = "I was created at compile time" };
    button.Click += CreateButtonEvent;
    sp.Children.Add(button);
    

    It's just two different ways of doing the same thing.

    Theoretically, a XAML compiler could just output C# code that you could then include and compile in your project and your application would work exactly the same way it does now. Indeed, when you make a UserControl, for example, the compiler does actually output some C# code that gets executed when InitializeComponent is called in the constructor; you can find the generated files in the obj directory of your project after compilation. That's how the x:Name'd items in your XAML become private class members that you can reference in your code-behind. (This is also why all your UserControl's are partial classes). Similarly, the Xamarin XAML compiler has an option to compile XAML and inject it directly into your assembly as IL (that's the bytecode that C# compiles into); this is their answer to BAML, more or less.

    Even templates can be created through C# with the rarely-used FrameworkElementFactory. (I wouldn't recommend it, but it can be done!) If you really want to get technical and into the weeds, FrameworkElementFactory is actually what's used when the BAML is processed at runtime to build the visual tree.

    So to get back to your direct question, declaring something in XAML doesn't actually create it. It creates instructions to the Framework that allow it to create the objects at runtime. Consequently, everything is created at runtime; some through XAML compiled to BAML, some through XAML compiled to C#, and some through straight C#. Thus there is ultimately no difference between adding buttons, or any other kind of visual element, through XAML or through your C# code. It all depends on what's best for your use case.