《深入浅出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 学习笔记

视频资料地址

扫描二维码关注公众号,回复: 15423392 查看本文章

《深入浅出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。来进行页面样式的显示或者不显示。

声明页面元素
修改Html样式
动态html对应的CSS
实现业务逻辑
原则上不能直接操控Html
的元素生成
原则上不能修改CSS的样式内容
用户页面
Html
CSS
JS
<!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中的内容关系如下

负责所有的页面元素
样式和简单的用户交互
C#
复杂的交互逻辑
业务逻辑
Xmal
负责Style管理和资源管理
windows.Resource
Style
负责样式管理,触发器管理
xmal元素
负责声明元素
用户页面
数据交互和触发事件回调

而xmal里面,元素的是可以沟通的,在Web端则不行。Web需要通过JS事件触发来修改数据。这个就是事件驱动型。事件驱动型认为所有的页面交互都是由事件触发的,两个页面元素的沟通必须要有一个中间事件。

修改对应元素值
页面A数据修改
触发JS函数
B元素修改

Xmal中,则是数据驱动型,或者可以看做匿名事件。就是我没必要声明一个事件,我直接将值给他就行了

A被修改属性
xmal元素A
xmal元素B联动

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)。

猜你喜欢

转载自blog.csdn.net/qq_44695769/article/details/131348652