文章目录
- 相关资源
- 前言
- WPF 学习笔记
- 环境配置
- WPF基础:一个WPF程序是如何启动的
- xmal文件和cs文件是如何连接的
- 如何确定启动页面
- xmal文件如何引用别的文件
- 如何引用
- WPF是如何创建元素,改变元素的
- WPF的元素创建和简单属性赋值
- WPF的树形界面
- Xmal属性赋值
- 为什么要多种属性赋值
- 属性转换
- 解决方法
- 属性标签
- 属性标签的特点和缺点
- 属性标签的缺点
- 扩展标签
- WPF的属性绑定
- 页面元素相互赋值
- WPF 数据绑定会在后面的Prism框架中进行详细说明。
- WPF组件化
相关资源
《深入浅出WPF》系列高清视频教程 | 讲师:刘铁猛
深入浅出wpf pdf版完整版下载
刘铁猛老师B站号。但是只有最近的视频资料
前言
WPF 是一个设计非常超前的技术框架,即使他是在2007年开始研发,2010年正式发行的技术。里面的MVVM,业务代码和界面代码完全分离,低耦合,高内聚的思想一直影响着后来的前端发展。比如Vue就是MVVM的继承的。
WPF在国内就业市场的情况并不理想,原因是桌面软件的式微,功能逐渐从PC端转向WEB端。我用网页能解决的,为什么要下个客户端呢?WPF技术的推广也是个问题。因为传统互联网的崛起,把java抬到了一个非常高的高度,java的轮子特别多,生态特别好,学的人也特别多。
所以我认为,不要以为学了WPF就可以找到很好的工作了。.NET工程师还是要转全栈,前端后端都要会。我目前觉得最简单的全栈技术栈是Vue+uniapp+WPF+webApi+.NET core。数据库:Sql server sqlite Mysql。其实最好再学个Unity 2d+Unity 3d +C4D。这样就是完整的全栈了。基本一个简单的小项目你都可以解决。
我今年的模板就是学会WPF+Unity 2d+C4D建模。我学习的逻辑一直都是从低到高,就是先学会工具怎么用,再去钻研高深的东西。这部分学好了,明年就开始学linux和python。
C#和JAVA是相互竞争的关系,我暂时没有精力去学Java的知识。等我需要用到的时候再说吧
虽然有点离题了,回到深入浅出WPF ,刘铁猛老师还是很猛的,讲解的特别好。不像别的课,上来先讲个几个小时的控件属性。直接从原理开始讲,从底层到应用,特别的清晰。
WPF 学习笔记
视频资料地址
《深入浅出WPF》系列高清视频教程 | 讲师:刘铁猛
环境配置
- .NET Core 6.0
- WPF
WPF基础:一个WPF程序是如何启动的
我们创建的一个简单的WPF程序。
xmal文件和cs文件是如何连接的
WPF为了做到页面和业务分离,强制了我们只能在xmal里面写页面,在cs文件里面写业务。这个强制分离是为了降低耦合。如果有同学试过维护JQuery的项目就知道业务和页面不分离是多么痛苦的事情。一个函数里面包含了页面逻辑和业务逻辑,导致后期更改的时候要完全理解整个函数是怎么跑的,随便删一个变量就直接报错。
我们以App.xmal和App.xaml.cs为例
如何确定启动页面
xmal文件如何引用别的文件
如何引用
WPF是如何创建元素,改变元素的
WPF进行了高度的页面和业务分割,然后通过编译,将对应的页面和文件进行映射。所有我们所有的元素创建和简单的交互事件都在xmal进行。
WPF的元素创建和简单属性赋值
以一个简单的按钮为例
在布局元素中创建控件。
<Grid><Button Content="Show Msg!"//显示的文字Click="Button_Click"//点击事件Width="100"//宽度Height="30" />//高度
</Grid>
被<>符合包裹住的是标签,<>后续跟着的就是属性。在上面的例子中,Button是标签,Grid也是标签。Content、Click、Width、Height都是属性。
实现效果
WPF的树形界面
WPF的xmal和html很像,都是标记性语言
这个树形层级结构就是WPF的层级。
如果想具体了解,可以去看我之前写的文章。
WPF 如何实时查看页面元素如何使用实时可视化树
Xmal属性赋值
Xmal一共有三种属性赋值的方式。
- 行内标签:默认的赋值方式
- 属性标签:字符串输入转换数据类型
- 扩展标签
为什么要多种属性赋值
我们看一个最简单的Button按钮
<Grid><Button Content="Show Msg!"//字符串标签Click="Button_Click"Width="100"//属性标签Height="30" />
</Grid>
我们可以看到,我们赋值的值都是字符串。像Content就肯定是字符串,但是Width和Height,属性值应该是数字,但是我们输入的也是字符串。那就说明一点:我们可以对复制的字符串进行转换。
属性转换
属性转换的目的是为了我们通过标签属性去生成一些复杂的信息。比如Height = “100”。Height赋值一个字符串,里面肯定转换成了一个数字。
我们新建一个Person类
MainWindow.cs
namespace WpfLearnTest
{public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e)//按钮事件{///找到资源字典中名字为person的类Person _model = (Person)this.FindResource("person");MessageBox.Show(_model.Name);}}public class Person{public string Name { get; set; }public Person Chil { get; set; }}
}
MainWindow.xmal
<Window x:Class="WpfLearnTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfLearnTest"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Window.Resources><local:Person x:Key="person" Name="小王" />//声明Person类</Window.Resources><Grid><Button Content="Show Msg!"Click="Button_Click"//添加点击按钮Width="100"Height="30" /></Grid>
</Window>
实现结果
成功的原因是因为我们的Person类的Name是字符串,但是如果不是字符串会如何?
我们可以的看到Child报错。字符串不能直接转Person类。
解决方法
///引入属性转换[TypeConverterAttribute(typeof(NameToPersonTypeConverter))]public class Person{public string Name { get; set; }public Person Child { get; set; }}/// <summary>/// 添加属性转换/// </summary>public class NameToPersonTypeConverter : TypeConverter{public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value){string name = value.ToString();Person child = new Person();child.Name = name;return child;}}
使用效果:
运行结果成功!
属性标签
需要解决的问题。
在网页端,Html+css中是如何实现的?
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>一个简单的网页</title></head><body><div><button style="width: 100px;height: 30px;"><div style="height: 10px;min-width: 10px;border: 1px solid red;display: inline-block;"></div></button></div></body>
</html>
在xmal中也可以使用嵌套
<Window x:Class="WpfLearnTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfLearnTest"mc:Ignorable="d"Title="MainWindow"Height="450"Width="800"><Grid><Button Width="100"Height="30"><Rectangle Width="20"Height="20"Stroke="DarkGreen"Fill="LawnGreen" /></Button></Grid>
</Window>
但是xmal支持我们使用属性标签 的方式填写
<Button Click="Button_Click"Width="100"Height="30"><Button.Content>//属性标签,对应button的content值<Rectangle Width="20"Height="20"Stroke="DarkGreen"Fill="LawnGreen" /></Button.Content></Button>
实现的效果是一样的
那嵌套一个属性标签到底有什么用呢?
结论是在这个案例里面没有区别。
在实时可视化树中也没区别
属性标签的特点和缺点
我们现在有一个需求
<Rectangle Width="200"Height="160"Stroke="Blue"Fill="LightBlue" />
我们看看html+css是如何解决的
网页端用html+css+JS分离。只让html声明元素,让css修改样式。JS在使用中原则上不添加或者删除html元素(虽然可以进行DOM操作实现,但是这样会将提高耦合,容易形成屎山代码),而是通过css的display:none。来进行页面样式的显示或者不显示。
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>一个简单的网页</title>
</head>
<body><div id="box"></div>
</body>
</html>
<style type="text/css">#box {height: 200px;background-color: red;background-image: linear-gradient(to bottom right, red, blue);}
</style>
实现效果
属性标签的缺点
我们看看WPF实现上述效果要怎么写?
<Rectangle Width="200"Height="160"Stroke="Blue"><Rectangle.Fill><LinearGradientBrush><LinearGradientBrush.StartPoint><Point X="0"Y="0" /></LinearGradientBrush.StartPoint><LinearGradientBrush.EndPoint><Point X="1"Y="1" /></LinearGradientBrush.EndPoint><LinearGradientBrush.GradientStops><GradientStop Offset="0.2"Color="LightBlue" /><GradientStop Offset="0.7"Color="DarkBlue" /><GradientStop Offset="1.0"Color="LightBlue" /></LinearGradientBrush.GradientStops></LinearGradientBrush></Rectangle.Fill></Rectangle>
我们可以看到只是个单纯的渐变效果,就写的特别麻烦
当然,我们可以使用行内标签来进行优化
<Rectangle Width="200"Height="160"Stroke="Blue"><Rectangle.Fill><LinearGradientBrush StartPoint="0,0" EndPoint="1,1">//使用行内标签来对代码进行优化<LinearGradientBrush.GradientStops><GradientStop Offset="0.2"Color="LightBlue" /><GradientStop Offset="0.7"Color="DarkBlue" /><GradientStop Offset="1.0"Color="LightBlue" /></LinearGradientBrush.GradientStops></LinearGradientBrush></Rectangle.Fill></Rectangle>
我们可以使用资源字典来优化
<Window.Resources><Style x:Key="MyButton"TargetType="Button"><Style.Setters><Setter Property="Content"Value="" /></Style.Setters></Style><Style x:Key="MyRectFill" TargetType="Rectangle"><Style.Setters><Setter Property="Fill" ><Setter.Value><LinearGradientBrush StartPoint="0,0"EndPoint="1,1"><GradientStop Offset="0.2"Color="LightBlue" /><GradientStop Offset="0.7"Color="DarkBlue" /><GradientStop Offset="1.0"Color="LightBlue" /></LinearGradientBrush></Setter.Value></Setter></Style.Setters></Style></Window.Resources><Grid><Rectangle Width="200"Height="160"Stroke="Blue" Style="{StaticResource MyRectFill}" ></Rectangle></Grid>
我们保证了页面元素的简洁化,但是由于WPF运行的关系,Window.Resources只能放在页面元素之前。而且由于C#和Xmal是强定义语言,所以写起来一定要对元素内容进行声明。
CSS里面5行能解决的事情,在Xmal里面有10行才能解决。
扩展标签
我们刚刚写的代码中
<Rectangle Width="200"Height="160"Stroke="Blue" Style="{StaticResource MyRectFill}" >//这个就是扩展标签</Rectangle>
扩展标签使用{}的形式,将属性值包含在里面。这时候我们输入的就不是字符串,而是一个属性对象。如果同学们学过Vue 就能了解到这个类似于Vue 的差值表达式{{value}}。
标签扩展一般和属性绑定配合,我过会会讲解如何使用属性绑定。这个是WPF的重点。
WPF的属性绑定
页面元素相互赋值
我之前说过WPF中的内容关系如下
而xmal里面,元素的是可以沟通的,在Web端则不行。Web需要通过JS事件触发来修改数据。这个就是事件驱动型。事件驱动型认为所有的页面交互都是由事件触发的,两个页面元素的沟通必须要有一个中间事件。
Xmal中,则是数据驱动型,或者可以看做匿名事件。就是我没必要声明一个事件,我直接将值给他就行了
xmal代码
<Grid><Grid.RowDefinitions><RowDefinition Height="24" /><RowDefinition Height="4" /><RowDefinition Height="24" /></Grid.RowDefinitions><TextBox x:Name="tb"Text="{Binding ElementName=sld,Path=Value}" /><Slider x:Name="sld"Grid.Row="2"Value="50"Maximum="100"Minimum="0" /></Grid>
WPF 数据绑定会在后面的Prism框架中进行详细说明。
WPF组件化
组件化,即将一个会重复复用界面封装成一个组件。WPF一共有一下4个类
- 窗口:一个窗口。窗口之间不能嵌套
- 页:很少用,也不知道干什么的,好像是网页的。
- 用户控件:套在窗口上面的。用于抽象化组件
- 资源字典:用于管理WPF资源的,比如样式,控件。
我们新建一个WPF资源类
新建项目
然后添加如下代码
UserControl1.xmal
<UserControl x:Class="ControlLibrary.UserControl1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:ControlLibrary"mc:Ignorable="d"d:DesignHeight="160"d:DesignWidth="240"Background="LightBlue"><Canvas><TextBox Canvas.Left="110"TextWrapping="Wrap"x:Name="textBox1"Canvas.Top="10"Width="120"HorizontalAlignment="Left"VerticalAlignment="Center"/><TextBox Canvas.Left="110"TextWrapping="Wrap"x:Name="textBox3"Canvas.Top="105"Width="120"HorizontalAlignment="Left"VerticalAlignment="Top" /><TextBox Canvas.Left="110"TextWrapping="Wrap"x:Name="textBox2"Canvas.Top="63"Width="120"HorizontalAlignment="Left"VerticalAlignment="Center" /><TextBlock TextWrapping="Wrap"Canvas.Top="10"Canvas.Left="10"><Run Language="zh-cn"Text="num1" /></TextBlock><TextBlock TextWrapping="Wrap"Canvas.Top="63"Canvas.Left="10"HorizontalAlignment="Center"VerticalAlignment="Top"><Run Text="num" /><Run Language="zh-cn"Text="2" /></TextBlock><TextBlock TextWrapping="Wrap"Canvas.Top="107"Canvas.Left="10"HorizontalAlignment="Center"VerticalAlignment="Top"><Run Language="zh-cn"Text="总计" /></TextBlock><Button Content="计算"Canvas.Left="126"Canvas.Top="136"HorizontalAlignment="Left"VerticalAlignment="Center"Width="88"Click="Button_Click" /></Canvas>
</UserControl>
UserControl.xmal.cs
using System;
......namespace ControlLibrary
{/// <summary>/// UserControl1.xaml 的交互逻辑/// </summary>public partial class UserControl1 : UserControl{public UserControl1(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e){this.textBox3.Text = ( int.Parse(this.textBox1.Text) + int.Parse(this.textBox2.Text) ).ToString();}}
}
然后添加对其的引用
每个项目都重新生成一下
在主函数中引用
<Window x:Class="WpfLearnTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfLearnTest"xmlns:sys="clr-namespace:System.Security.Claims;assembly=mscorlib"xmlns:controls="clr-namespace:ControlLibrary;assembly=ControlLibrary"mc:Ignorable="d"Title="MainWindow"Height="450"Width="800"><Grid><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions><controls:UserControl1 Grid.Row="0"Grid.Column="0" /><controls:UserControl1 Grid.Row="1"Grid.Column="1" /><controls:UserControl1 Grid.Row="1"Grid.Column="0" /><controls:UserControl1 Grid.Row="0"Grid.Column="1" /></Grid>
</Window>
使用结果
可以直接引入,而且能够执行用户控件的代码(总计的结果为num1+num2)。