文章目录
- 相关资源
- 前言
- 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)。