【.NET深呼吸】将XAML放到WPF程序之外
阅读原文时间:2023年08月22日阅读:1

上一篇水文中,老周说了一下纯代码编写 WPF 的大概过程。不过,还是不够的,本篇水文中咱们还要更进一步。

XAML 文件默认是作为资源打包进程序中的,而纯代码编写又导致一些常改动的东西变成硬编码了。为了取得二者平衡,咱们还要把一些经常修改的东西放到 XAML 文件中,不过 XAML 文件不编译进程序里,而是放到外部,运行阶段加载。比如一些对象属性、画刷、样式、字体之类的,直接改文件保存就行,修改之后不用重新编译项目。

要在运行阶段加载 XAML,咱们只需认识一个类就OK—— XamlReader,调用它的 Load 方法就能从 XAML 文件加载对象了。

下面老周就边演示边唠叨一下相关的问题。

一、新建项目。可以参照上一篇中的做法,用控制台应用程序项目,然后修改项目文件。也可以直接建 WPF 项目。都可以。

二、自定义窗口类,从 Window 派生。当然,你直接用 Window 类也可以的。

public class MyWindow : Window
{
const string XAML_FILE = "MyWindow.xaml";

public MyWindow()  
{  
    Title = "加载外部XAML";  
    Height = 150;  
    Width = 225;  
    // 从XAML文件加载  
    using FileStream fsIn = new(XAML\_FILE, FileMode.Open, FileAccess.Read);  
    FrameworkElement layout = (FrameworkElement)XamlReader.Load(fsIn);  
    // 两个按钮要处理事件  
    Button btn1 = (Button)layout.FindName("btn1");  
    Button btn2 = (Button)layout.FindName("btn2");  
    btn1.Click += OnClick1;  
    btn2.Click += OnClick2;  
    Content = layout;  
}

private void OnClick2(object sender, RoutedEventArgs e)  
{  
    MessageBox.Show("第二个按钮");  
}

private void OnClick1(object sender, RoutedEventArgs e)  
{  
    MessageBox.Show("第一个按钮");  
}  

}

这个不复杂,咱们关注加载 XAML 部分。通过文件流 FileStream 读取文件,而后在 XamlReader.Load 中加载。Load 方法返回的是 object 类型的对象,咱们要适当地进行类型转换。这个例子里面其实加载上来的是 Grid 类,但这里我只转换为 FrameworkElement 就可以了,毕竟我后面只用到了 FindName 方法。Find 出来的是两个 Button 对象,最后处理一下 Click 事件。

三、在项目中添加 MyWindow.xaml 文件。

四、在项目中添加 styles.xaml。

里面包含一个 Button 控件模板。

五、在 Main 方法中,初始化 Application 类,并且从外部 XAML 中加载资源字典。

[STAThread]
static void Main(string[] args)
{
Application app = new();
using FileStream extFile = new FileStream("styles.xaml", FileMode.Open, FileAccess.Read);
ResourceDictionary dic = (ResourceDictionary)XamlReader.Load(extFile);
app.Resources = dic;
app.Run(new MyWindow());
}

由于是在 app 处加载的资源,所以按钮样式会应用到整个程序。

六、打开项目文件(*.csproj),我们要做点手脚。


老周解释一下加了颜色的部分。

1、Page 表示XAML文件最终生成二进制文件,且塞进目标程序集中。加了 Remove 表示排除这一行为。说人话就是:本项目不编译 XAML 文件。

2、None 表示该项目中 XAML 文件“啥也不是”,编译时不做任何处理。

3、PostBuild 任务指定一条命令,在生成项目之后执行。此处是把项目目录下的 XAML 文件复制到输出目录。$(OutDir) 在 VS 中表示宏(也是 MSBuild 的属性)。在命令实际执行时,替换为实际目录路径,如 bin\Debug\net7.0-windows。

也可以用 $(TargetDir),不过 TargetDir 替换的是完整路径,OutDir 是用相对路径的。

现在生成一下项目,若没有问题,在输出目录下除了程序文件,还有那两个 XAML 文件。运行一下。

关闭程序,用记事本打开 styles.xaml 文件,把按钮的背景色改成橙色。

保存并关闭文件,重新运行程序。

咱们并没有重新编译程序。接下来用记事本打开 MyWindow.xaml 文件,改一下按钮上的文本。

保存并关闭文件,不用编译代码,再次运行程序。

这样就很方便修改了,不必每次都重新编译。

下一篇老周还会说说纯代码写 WPF 的模板问题。三维图形就看心情了。因为 3D 图形的构造和一般控件应用差不多,就是用代码建立 WPF 对象树。