首页 > 解决方案 > 如何在编译的代码中动态创建图形元素(.NET 5)?

问题描述

XAML 项目文件被编译成 XAML 代码的二进制表示 - 二进制应用程序标记语言 (BAML)。然后将 BAML 代码作为资源嵌入到应用程序的最终程序集中 - exe 或 dll 文件。它涉及 XAML。但是,在应用程序运行时在 C# 代码 (!) 中创建的图形元素呢?

假设应用程序是一个空窗口:

<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>

此 XAML 被编译为可执行资源。

在鼠标事件中,我添加了新按钮 - 是一个图形元素,在构建应用程序期间它没有被编译成 BAML:

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

在这方面,问题是:当我在应用程序运行时在 C# 代码(不是 XAML 代码)中创建图形项(按钮)时,技术上会发生什么?它是在写入由 BAML 创建的共享资源,还是在创建自己的 JIT 已经在读取的临时资源?谢谢

标签: c#wpfxamlcompilationbaml

解决方案


这是一个非常有趣的问题,我认为理解答案确实可以让您更深入地了解 WPF(以及 UWP 或 Xamarin)如何在幕后工作。

本质上,XAML 有一个简单的功能——告诉框架如何构建可视化树。因此,可以在 XAML 中完成的任何事情都可以转换为 C#;我想不出一个例外。举个例子:

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

可以写成:

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

这只是做同一件事的两种不同方式。

从理论上讲,XAML 编译器可以只输出 C# 代码,然后您可以在项目中包含并编译这些代码,并且您的应用程序的工作方式与现在完全相同。UserControl实际上,例如,当您制作 a 时,编译器确实InitializeComponent会输出一些在构造函数中调用时会执行的 C# 代码;你可以obj在编译后在你的项目目录中找到生成的文件。这就是x:NameXAML 中的 'd 项如何成为可以在代码隐藏中引用的私有类成员的方式。(这也是为什么你所有UserControlpartial类)。同样,Xamarin XAML 编译器具有编译 XAML 并将其作为 IL 直接注入程序集的选项(这是 C# 编译成的字节码);这或多或少是他们对 BAML 的回答。

甚至可以通过 C# 使用很少使用的FrameworkElementFactory. (我不推荐它,但它可以做到!)如果你真的想获得技术并进入杂草,FrameworkElementFactory实际上是在运行时处理 BAML 以构建可视化树时使用的。

因此,回到您的直接问题,在 XAML 中声明某些内容实际上并没有创建它。它为框架创建指令,允许它在运行时创建对象。因此,一切都是在运行时创建的;一些通过 XAML 编译为 BAML,一些通过 XAML 编译为 C#,还有一些通过直接 C#。因此,通过 XAML 或通过 C# 代码添加按钮或任何其他类型的可视元素最终没有区别。这一切都取决于最适合您的用例。


推荐阅读