翼度科技»论坛 编程开发 .net 查看内容

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

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
上一篇水文中,老周说了一下纯代码编写 WPF 的大概过程。不过,还是不够的,本篇水文中咱们还要更进一步。
XAML 文件默认是作为资源打包进程序中的,而纯代码编写又导致一些常改动的东西变成硬编码了。为了取得二者平衡,咱们还要把一些经常修改的东西放到 XAML 文件中,不过 XAML 文件不编译进程序里,而是放到外部,运行阶段加载。比如一些对象属性、画刷、样式、字体之类的,直接改文件保存就行,修改之后不用重新编译项目。
要在运行阶段加载 XAML,咱们只需认识一个类就OK—— XamlReader,调用它的 Load 方法就能从 XAML 文件加载对象了。
下面老周就边演示边唠叨一下相关的问题。
一、新建项目。可以参照上一篇中的做法,用控制台应用程序项目,然后修改项目文件。也可以直接建 WPF 项目。都可以。
 
 
二、自定义窗口类,从 Window 派生。当然,你直接用 Window 类也可以的。
  1. public class MyWindow : Window
  2. {
  3. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  4.    
  5. </ResourceDictionary>const string XAML_FILE = "MyWindow.xaml";
  6. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  7.    
  8. </ResourceDictionary>public MyWindow()
  9. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  10.    
  11. </ResourceDictionary>{
  12. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  13.    
  14. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  15.    
  16. </ResourceDictionary>Title = "加载外部XAML";
  17. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  18.    
  19. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  20.    
  21. </ResourceDictionary>Height = 150;
  22. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  23.    
  24. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  25.    
  26. </ResourceDictionary>Width = 225;
  27. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  28.    
  29. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  30.    
  31. </ResourceDictionary>// 从XAML文件加载
  32. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  33.    
  34. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  35.    
  36. </ResourceDictionary>using FileStream fsIn = new(XAML_FILE, FileMode.Open, FileAccess.Read);
  37. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  38.    
  39. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  40.    
  41. </ResourceDictionary>FrameworkElement layout = (FrameworkElement)XamlReader.Load(fsIn);
  42. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  43.    
  44. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  45.    
  46. </ResourceDictionary>// 两个按钮要处理事件
  47. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  48.    
  49. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  50.    
  51. </ResourceDictionary>Button btn1 = (Button)layout.FindName("btn1");
  52. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  53.    
  54. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  55.    
  56. </ResourceDictionary>Button btn2 = (Button)layout.FindName("btn2");
  57. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  58.    
  59. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  60.    
  61. </ResourceDictionary>btn1.Click += OnClick1;
  62. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  63.    
  64. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  65.    
  66. </ResourceDictionary>btn2.Click += OnClick2;
  67. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  68.    
  69. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  70.    
  71. </ResourceDictionary>Content = layout;
  72. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  73.    
  74. </ResourceDictionary>}
  75. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  76.    
  77. </ResourceDictionary>private void OnClick2(object sender, RoutedEventArgs e)
  78. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  79.    
  80. </ResourceDictionary>{
  81. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  82.    
  83. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  84.    
  85. </ResourceDictionary>MessageBox.Show("第二个按钮");
  86. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  87.    
  88. </ResourceDictionary>}
  89. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  90.    
  91. </ResourceDictionary>private void OnClick1(object sender, RoutedEventArgs e)
  92. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  93.    
  94. </ResourceDictionary>{
  95. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  96.    
  97. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  98.    
  99. </ResourceDictionary>MessageBox.Show("第一个按钮");
  100. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  101.    
  102. </ResourceDictionary>}
  103. }
复制代码
这个不复杂,咱们关注加载 XAML 部分。通过文件流 FileStream 读取文件,而后在 XamlReader.Load 中加载。Load 方法返回的是 object 类型的对象,咱们要适当地进行类型转换。这个例子里面其实加载上来的是 Grid 类,但这里我只转换为 FrameworkElement 就可以了,毕竟我后面只用到了 FindName 方法。Find 出来的是两个 Button 对象,最后处理一下 Click 事件。
 
三、在项目中添加 MyWindow.xaml 文件。
  1. <Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  3.    
  4. </ResourceDictionary>  Margin="12">
  5. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  6.    
  7. </ResourceDictionary><Grid.RowDefinitions>
  8. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  9.    
  10. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  11.    
  12. </ResourceDictionary><RowDefinition Height="auto"/>
  13. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  14.    
  15. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  16.    
  17. </ResourceDictionary><RowDefinition Height="auto"/>
  18. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  19.    
  20. </ResourceDictionary></Grid.RowDefinitions>
  21. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  22.    
  23. </ResourceDictionary><Button Name="btn1" Grid.Row="0" Margin="5,8">按钮A</Button>
  24. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  25.    
  26. </ResourceDictionary><Button Name="btn2" Grid.Row="1" Margin="5,8">按钮B</Button>
  27. </Grid>
复制代码
这里顺便说一下,保存 XAML 文件时最好用 UTF-8 编码,不然可能会报错。方法是在 VS 里,【文件】-【XXX 另存为】。在保存文件对话框中,点“保存”按钮右边的箭头,选择“编码保存”。

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

另一种方法是用记事本打开,再以 UTF-8 保存。
 
四、在项目中添加 styles.xaml。
  1. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  2.    
  3. </ResourceDictionary>
复制代码
里面包含一个 Button 控件模板。
 
五、在 Main 方法中,初始化 Application 类,并且从外部 XAML 中加载资源字典。
  1. [STAThread]static void Main(string[] args){<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  2.    
  3. </ResourceDictionary>Application app = new();<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  4.    
  5. </ResourceDictionary>using FileStream extFile = new FileStream("styles.xaml", FileMode.Open, FileAccess.Read);<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  6.    
  7. </ResourceDictionary>ResourceDictionary dic = (ResourceDictionary)XamlReader.Load(extFile);<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  8.    
  9. </ResourceDictionary>app.Resources = dic;<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  10.    
  11. </ResourceDictionary>app.Run(new MyWindow());}
复制代码
由于是在 app 处加载的资源,所以按钮样式会应用到整个程序。
 
六、打开项目文件(*.csproj),我们要做点手脚。
  1. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  2.    
  3. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  4.    
  5. </ResourceDictionary><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  6.    
  7. </ResourceDictionary>
复制代码
老周解释一下加了颜色的部分。
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 对象树。

来源:https://www.cnblogs.com/tcjiaan/archive/2023/06/16/17486037.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具