【.NET深呼吸】将XAML放到WPF程序之外

net,深呼吸,xaml,放到,wpf,程序,之外 · 浏览次数 : 567

小编点评

**问题一:如何将一些经常修改的东西放到 XAML 文件中?** 可以将一些对象属性、画刷、样式、字体之类的,直接改文件保存就行,修改之后不用重新编译项目。可以在 XAML 文件中定义这些属性、样式、字体等,并将其赋值给 FrameworkElement 对象。 **问题二:如何在 XAML 文件中加载对象?** 可以使用 `XamlReader.Load` 方法从 XAML 文件加载对象。该方法返回一个 `FrameworkElement` 对象,可以用于在运行程序时显示该对象。 **问题三:如何在项目中添加 XAML 文件?** 可以在 `App.xaml` 文件中添加 XAML 文件。可以使用 `ResourceDictionary` 等容器来存储 XAML 文件。 **问题四:如何在项目中添加样式?** 可以在 `App.xaml` 文件中添加样式。可以使用 `Style` 类来定义样式。 **问题五:如何在 Main 方法中初始化应用程序和加载 XAML 文件?** 可以在 `Main` 方法中初始化 `Application` 类,并使用 `XamlReader.Load` 方法从 XAML 文件中加载 `ResourceDictionary`。

正文

上一篇水文中,老周说了一下纯代码编写 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 文件。

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      Margin="12">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="auto"/>
    </Grid.RowDefinitions>
    <Button Name="btn1" Grid.Row="0" Margin="5,8">按钮A</Button>
    <Button Name="btn2" Grid.Row="1" Margin="5,8">按钮B</Button>
</Grid>

这里顺便说一下,保存 XAML 文件时最好用 UTF-8 编码,不然可能会报错。方法是在 VS 里,【文件】-【XXX 另存为】。在保存文件对话框中,点“保存”按钮右边的箭头,选择“编码保存”。

编码选 UTF-8 无签名(或带签名的也行)。

另一种方法是用记事本打开,再以 UTF-8 保存。

 

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

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <Style TargetType="Button">
        <Setter Property="Background" Value="Red"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="Padding" Value="5"/>
        <Setter Property="FontFamily" Value="楷体"/>
        <Setter Property="FontSize" Value="17"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="BorderBrush" Value="Yellow"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="5">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          Margin="{TemplateBinding Padding}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="Green"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter Property="Background" Value="DarkSlateGray"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

里面包含一个 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),我们要做点手脚。

<ItemGroup>
    <Page Remove="*.xaml"/>
    <None Include="*.xaml"/>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="copy /y *.xaml $(OutDir)" />
</Target>

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

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 对象树。

与【.NET深呼吸】将XAML放到WPF程序之外相似的内容:

【.NET深呼吸】将XAML放到WPF程序之外

上一篇水文中,老周说了一下纯代码编写 WPF 的大概过程。不过,还是不够的,本篇水文中咱们还要更进一步。 XAML 文件默认是作为资源打包进程序中的,而纯代码编写又导致一些常改动的东西变成硬编码了。为了取得二者平衡,咱们还要把一些经常修改的东西放到 XAML 文件中,不过 XAML 文件不编译进程序

2023版:深度比较几种.NET Excel导出库的性能差异

引言 背景和目的 本文介绍了几个常用的电子表格处理库,包括EPPlus、NPOI、Aspose.Cells和DocumentFormat.OpenXml,我们将对这些库进行性能测评,以便为开发人员提供实际的性能指标和数据。 下表将功能/特点、开源/许可证这两列分开,以满足需求: 功能 / 特点 EP

[转帖]学习下 Redis 内存模型

https://my.oschina.net/u/4090830/blog/5747217 前言 redis,对于一个 java 开发工程师来讲,其实算不得什么复杂新奇的技术,但可能也很少人去深入了解学习它的底层的一些东西。下面将通过对内存统计、内存划分、存储细节、对象类型 & 内部编码这四个模块来

WTM的项目中EFCore如何适配人大金仓数据库

一、WTM是什么 WalkingTec.Mvvm框架(简称WTM)最早开发与2013年,基于Asp.net MVC3 和 最早的Entity Framework, 当初主要是为了解决公司内部开发效率低,代码风格不统一的问题。2017年9月,将代码移植到了.Net Core上,并进行了深度优化和重构,

TCP内核参数的简单验证

前言 春节假期时学习了下内核参数与nginx的调优 最近因为同事遇到问题一直没有解,自己利用晚上时间再次进行验证. 这里将几个参数的理解和验证结果简单总结一下. 希望能够在学习的过程中将问题解决掉. 其实很后悔没有好好学习代码.现在很多问题都已经到了瓶颈期 无法深入的研究下去. 参数一 net.ip

【.NET 深呼吸】全代码编写WPF程序

学习 Code 总有这样一个过程:入门时候比较依赖设计器、标记语言等辅助工具;等到玩熟练了就会发现纯代码写 UI 其实更高效。而且,纯代码编写也是最灵活的。Windows Forms 项目是肯定可以全代码编写的,哪怕你使用了设计器,它最后也是生成代码文件;而 WPF 就值得探索一下了。咱们知道,WP

【.NET深呼吸】用代码写WPF控件模板

这一次咱们来探究一下怎么用纯代码写 WPF 模板。模板有个共同基类 FrameworkTemplate,数据模板、控件模板等是从此类派生的,因此,该类已定义了一些通用成员。 用代码构建模板,重要的成员是 VisualTree 属性,它的类型是 FrameworkElementFactory。可见,模

C#.Net筑基-深入解密小数内部存储的秘密

为什么0.1 + 0.2 不等于 0.3?为什么16777216f 等于 16777217f?为什么金钱计算都推荐用decimal?本文主要学习了解一下数字背后不为人知的存储秘密。

[转帖]【mmap】深度分析mmap:是什么 为什么 怎么用 性能总结

`https://blog.csdn.net/bandaoyu/article/details/106750990` 目录 有什么用? 1、文件映射 2、分配内存(匿名文件映射) mmap基础概念 mmap内存映射原理 mmap和常规文件操作的区别 mmap优点总结 mmap相关函数 mmap使用细

C#.Net筑基-String字符串超全总结 [深度好文]

字符串是日常编码中最常用的引用类型了,可能没有之一,加上字符串的不可变性、驻留性,很容易产生性能问题,因此必须全面了解一下。