【.NET深呼吸】将XAML放到WPF程序之外
上一篇水文中,老周说了一下纯代码编写 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>
四、在项目中添加 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程序之外的更多相关文章
- 11、创建不使用XAML的WPF应用程序
首先新建一个空的项目,然后添加一个类,引用一下程序集: PresentationCore.dll PresentationFramework.dll WindowsBase.dll namespace ...
- WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?
原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...
- 在WPF程序中打开网页:使用代理服务器并可进行JS交互
本项目环境:使用VS2010(C#)编写的WPF程序,通过CefSharp在程序的窗体中打开网页.需要能够实现网页后台JS代码中调用的方法,从网页接收数据,并能返回数据给网页.运行程序的电脑不允许上网 ...
- 提高WPF程序性能的几条建议
这篇博客将介绍一些提高WPF程序的建议(水平有限,如果建议有误,请指正.) 1. 加快WPF程序的启动速度: (1).减少需要显示的元素数量,去除不需要或者冗余的XAML元素代码. (2).使用UI虚 ...
- 为WPF程序添加字体
很多时候我们开发的程序可能会在多个版本的Windows上运行,比如XP.Win7.Win8. 为了程序美观,现在很多公司会使用WPF作为程序的界面设计. 跨版本的操作的操作系统往往有一些字体上的问题, ...
- WPF 程序的编译过程
原文:WPF 程序的编译过程 基于 Sdk 的项目进行编译的时候,会使用 Sdk 中附带的 props 文件和 targets 文件对项目进行编译.Microsoft.NET.Sdk.WindowsD ...
- WPF程序将DLL嵌入到EXE的两种方法
WPF程序将DLL嵌入到EXE的两种方法 这一篇可以看作是<Visual Studio 版本转换工具WPF版开源了>的续,关于<Visual Studio 版本转换工具WPF版开源了 ...
- WPF程序在Windows 7下应用Windows 8主题
这篇博客介绍如何在Windows 7下应用Windows 8的主题. 首先我们先看一个很常见的场景,同样的WPF程序(样式未重写)在不同的操作系统上展示会有些不同.这是为什么呢?WPF程序启动时会加载 ...
- 在WPF程序中使用摄像头兼谈如何使用AForge.NET控件(转)
前言: AForge.NET 是用C#写的一个关于计算机视觉和人工智能领域的框架,它包括图像处理.神经网络.遗传算法和机器学习等.在C#程序中使用摄像头,我习惯性使用AForge.NET提供的类库.本 ...
- WPF 程序中启动和关闭外部.exe程序
当需要在WPF程序启动时,启动另一外部程序(.exe程序)时,可以按照下面的例子来: C#后台代码如下: using System; using System.Collections.Generic; ...
随机推荐
- [Linux]查看硬件及操作系统信息
许多的软件产品对硬件及操作系统等环境是有具体要求的,那么这时候如何快速知晓目标机器的目标资源信息是较为频繁的操作. 命令 全部硬件及系统信息 dmidecode (软硬件全部信息) hostnamec ...
- [Linux]CentOS7:卸载、安装Java JDK
JDK(Java Development Kit)是Java语言的软件开发工具包,包括Java运行环境.Java开发工具.Java基础类库. JRE(Java Runtime Environment) ...
- Django笔记十七之group by 分组用法总结
本文首发于微信公众号:Hunter后端 原文链接:Django笔记十七之group by 分组用法总结 这篇笔记介绍 Django 里面 model 的 group by 对应的一些操作. 用到的 M ...
- 深度学习之PyTorch实战(5)——对CrossEntropyLoss损失函数的理解与学习
其实这个笔记起源于一个报错,报错内容也很简单,希望传入一个三维的tensor,但是得到了一个四维. RuntimeError: only batches of spatial targets supp ...
- C++ 测试框架 GoogleTest 初学者入门篇 丙
theme: channing-cyan *以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/RIztusI3uKRno ...
- 安装Zookeeper和Kafka集群
安装Zookeeper和Kafka集群 本文介绍如何安装Zookeeper和Kafka集群.为了方便,介绍的是在一台服务器上的安装,实际应该安装在多台服务器上,但步骤是一样的. 安装Zookeeper ...
- 【源码分析】XXL-JOB的执行器的注册流程
目的:分析xxl-job执行器的注册过程 流程: 获取执行器中所有被注解(@xxlJjob)修饰的handler 执行器注册过程 执行器中任务执行过程 版本:xxl-job 2.3.1 建议:下载xx ...
- Ffmpeg分布式视频转码问题总结
本文主要聊一聊云原生时代分布式转码系统实施过程中碰到的一些问题. 聊问题之前简单介绍一下我们的分布式转码方案. 云原生分布式转码 在计算资源招之即来的云计算时代,正在重构着软件架构的方方面面. 对软件 ...
- 读《mysql是怎样运行的》有感
最近读了一本书<mysql是怎样运行的>,读完后在大体上对mysql的运行有一定的了解.在以前,我对mysql有以下的为什么: InnoDB中的表空间.段.区和页是什么? redo log ...
- Java 新的生态型应用开发框架,Solon v2.2.13 发布
Java 新的生态型应用开发框架,Solon :更快.更小.更简单.从零开始构建,有自己的标准规范与开放生态: 150多个生态插件,可以满足各种场景开发 大量的国产框架适配,可以为应用软件国产化提供更 ...