WPF 深入浅出 第一部分

WPF 深入浅出

1 XAML概览

1.1 什么是XAML?

XAML是 WPF 技术中专门用于设计UI的语言

1.2 XAML 的优点

  • XAML可以设计出专业的UI和动画 (好用)

  • XAML不需要专业的编程知识,他简单易懂,结构清晰 (易学)

  • XAML使设计师能直接参与软件开发,随时沟通,无需二次转换 (高效)

  • 结构分离

2 从零起步认识XAML

2.1 新建WPF项目

image-20220719161821473

​ 创建一个WPF桌面应用程序 会自动生成一些列文件 如下:

  • Properties 分支:里面的主要内容是程序要用到的一些资源(如 图标,图片,静态的字符串) 和 配置信息

  • Reference 分支: 标记了当前这个项目需要引用那些其他项目 默认都是.NET Framework 中的类库

  • App.xaml 分支:程序的主体 ,大家都知道Window 系统里面,一个程序就是一个进程,Window还规定,一个GUI进程还需要有一个窗体(Window)作为"主窗体",App.xaml 文件的作用就是声明了程序的进程会是谁,同时指定了程序的主窗体是谁,在这个分支里面还有一个文件--App.xaml.cs 他是App.xaml的后台代码

  • MainWindow.xaml : 程序的主窗体

2.2 解析最简单的XAML代码

MainWindow.xaml 代码:

<Window x:Class="_001_xmal的认识.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:_001_xmal的认识"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" AutomationProperties.Name="MyFirstWpfApplication">
    <Grid>
        
    </Grid>
</Window>

写法: XAML 是 XML派生的,很多概念都通用 如果使用一个标签声明一个元素 如果标签没有内容可以 这样写

​ 为了表示同类标签中的某个标签与众不同,可以给他的特征(Attribute)赋值, 为特征赋值如下:

  • 非空标签: Content
  • 空标签

​ 在这里 有必要吧 Attribute 和 property 这两个词分辨一下 property 属于面向对象理论范畴 Attribute 是语言上的东西相关 ...

​ 他的总体结构是包裹 且里面有个 或者说 Grid标签是Window标签的内容,代表着一个窗口对象内嵌套这一个Grid对象

<Window>
	<Grid>
    
    </Grid>
</Window>

XAML是一种声明示语言,当你见到一个标签,就意味着声明了一个对象,对象之间的层级关系要么是并列,要么是包含

下面这些代码就是的标签的Attribute

<Window x:Class="_001_xmal的认识.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:_001_xmal的认识"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" AutomationProperties.Name="MyFirstWpfApplication">
</Window>

其中 Height Width Title AutomationProperties.Name 就是与Window 对象的 Property 相对应的

xmlns 是 (XML Namespace ) 的缩写, 作用是定义名称空间, 好处是 不同类重名时可以加名称空间来区分 xmlns特征的语法格式如下:

xmlns:[:可选的映射前缀]="名称空间"

xmlns 后可以跟一个可选的映射前缀,之间用冒号隔开

如果没写映射前缀的话,那就意味着所有来自这个名称空间的标签 都不用加前缀,称为"默认名称空间"---- 默认名称空间只能有一个!!!! (应该使用 使用最频繁的标签命名空间作为默认命名空间)

上面例子中, 都是来自于第二行的命名空间

而第一行中的Class 特征则来自于第三行的x:前缀对应的名称空间

实验:我们把xmlns加个前缀n 然后添加标签

<n:Window x:Class="_001_xmal的认识.MainWindow"
        xmlns:n="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:_001_xmal的认识"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" n:AutomationProperties.Name="wqe" >
    <n:Grid>
        
    </n:Grid>
</n:Window>

重点: XAML 中引用外来程序集 和 c# 是不一样的 c# using就行 但是XAML需要添加到引用中,然后通过 xmlns:前缀="clr-namespace: 命名空间;assembly=xxx"

例如使用 button 类

<Window
        xmlns:b="clr-namespace:System.Window.Controls;assembly=PresentationFramework">
</Window>

默认引进的名称空间: http://schemas.microsoft.com/winfx/2006/xaml/presentation

  • System.Windows
  • System.Automation
  • System.Windows.Controls
  • System.Windows.Controls.Primitives
  • System.Windows.Data
  • System.Windows.Documents
  • System.Windows.Forms.Integration
  • System.Windows.Ink
  • System.Windows.Input
  • System.Windows.Media
  • System.Windows.Media.Animation
  • System.Windows.Media.Effects
  • System.Windows.Media.Imaging
  • System.Windows.Media.Media3D
  • System.Windows.Media.TextFormatting
  • System.Windows.Navigation
  • System.Windows.Shapes
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 x:Class="_001_xmal的认识.MainWindow

上面的 xmlns 对应的名称空间 对应XAML语言解析处理相关的程序集

如果将 x:Class 改为其他类 或者 该XAML下同名.cs文件的 InitializeComponent() 注释 可能会出问题

但是App.xaml中 定义了 StartupUri="MainWindow.xaml" 他是在告诉编译器 吧MainWindow.xaml作为主窗体 ,只要能解析成一个窗体,那么程序就可以正常运行

如果注释 InitializeComponent() 修改 x:class="名称空间.其他类" 使用中间语言反编译器 可以看到 项目编译的程序集包含一个名为 其他类的类

重点: 这说明,x:Class 这个Attribute 的作用是当XAML 解析器 将包含他的标签解析成 c#类后,这个类的类名是啥

但是出现了一个问题 两个类重名了 注意看 partial 关键字 目的可以将 不同方法放到不同类中!将这个类一份为二 所以我们可以将 UI 和 逻辑分开

// 标注重点:
/*
	1.标签的定义
		空标签 和 非空标签 标签的Attribute 每一个标签代表一个对象
	2.名称空间
		xmlns="" 默认名称空间 只能有一个
		xmlns:x= "" 映射前缀
		xmlns:前缀="clr-namespace: 命名空间;assembly=xxx"  程序集的引入
		http://schemas.microsoft.com/winfx/2006/xaml/presentation 默认名称空间
		http://schemas.microsoft.com/winfx/2006/xaml  XMAL的解析 以及 x:Class 的解释
		partial 关键字
*/

3 系统学习 XAML语法

3.1 XAML 文档的树形结构

和 html 一样 是树形机构

3.2 XAML 中为对象属性赋值的语法

在编译过程中 XAML 会为每个标签创建一个与之对应的对象,对象创建出来之后需要对他的属性初始化之后才更有意义

XAML 中为对象属性赋值共有两种语法:

  • 使用字符串简单赋值
  • 使用属性元素 (Property Element) 进行复杂赋值

下面我们以一个 标签的 Fill 为例 介绍两种方法

3.2.1 使用标签的 Attribute 为对象属性赋值

通过MSDN文档库可以查到,Rectangle.Fill 的类型是 Brush Brush是一个抽象类, 凡是以Brush为基类的类都可以为Fill进行赋值

Brush的派生类:

  • SolidColorBrush: 单色画刷
  • LinearGradientBrush: 线性渐变画刷
  • RadialGradientBrush:经向渐变画刷
  • ImageBrush:位图画刷
  • DrawingBrush: 矢量图画刷
  • VisualBrush: 可视元素画刷
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
    <Rectangle x:Name="rectangle" Width="100" Height="100" Fill="Blue"/>
</Grid>
SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.Orange;
this.rectangle.Fill = brush;

有个案例 我把XMAL改为c#了

// 渐变
LinearGradientBrush linearGradient = new LinearGradientBrush();

linearGradient.StartPoint = new Point(0, 1);
linearGradient.EndPoint = new Point(1, 1);

linearGradient.GradientStops.Add(new GradientStop(Colors.LightBlue, 0.2));
linearGradient.GradientStops.Add(new GradientStop(Colors.Blue, 0.7));
linearGradient.GradientStops.Add(new GradientStop(Colors.DarkBlue, 1.0));
this.rectangle.Fill = linearGradient;
    <Grid VerticalAlignment="Center" HorizontalAlignment="Center">
        <Rectangle x:Name="rectangle" Width="100" Height="100">
            <Rectangle.Fill>
                <LinearGradientBrush>
                    <LinearGradientBrush.StartPoint>
                        <Point X="0" Y="0"/>
                    </LinearGradientBrush.StartPoint>
                    <LinearGradientBrush.EndPoint>
                        <Point X="1" Y="1"/>
                    </LinearGradientBrush.EndPoint>

                    <LinearGradientBrush.GradientStops>
                        <GradientStopCollection>
                            <GradientStop Offset="0.2" Color="LightBlue"/>
                            <GradientStop Offset="0.7" Color="Blue"/>
                            <GradientStop Offset="1.2" Color="DarkBlue"/>
                        </GradientStopCollection>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Grid>

LinearGradientBrush 的 GradientStops属性是一个 GradientStop的集合(GradientStopCollection) 即一系列的矢量渐变填充点,在这填充点之间,系统会自动进行差值预算,计算过度的彩色 填充矢量渐变的方向是 StartPoint 和 endPoint 两个属性 都是 Point 类型

3.2.2 标记扩展

有时候你会发现 你想给某Attribute 赋值 都会创建一个新的对象 但是如果使用的对象一样 或 一个对象赋值给两个属性,还有的时候需要给属性赋值Null 怎么搞?

所谓标记扩展 ,实际上是一种特殊的Attribute = Value 语法,其特殊的地方在于Value字符串是有一对 花括号及其括起来的内容组成

<StackPanel Background="LightSlateGray">
    <TextBox Text="{Binding ElementName=slider1,Path=Value,Mode=OneWay}" Margin="5"/>
    <Slider x:Name="slider1" Margin="5"/>
</StackPanel>

其中 Text="{Binding ElemnetName=slider1,path=Value,Mode=OneWay}" 就是标记扩展了

我们分析下这句代码:

  • 当编译器看到这句代码时就会把花括号里的内容解析成相应的对象
  • 对象的数据类型是紧邻左花括号的字符串
  • 对象的属性由一串以逗号连接的子字符串负责初始化
Binding binding = new Binding(){Source=slider1,Mode=BindingMode.OneWay}

下面使用属性标签替换标记扩展

<StackPanel Background="LightSlateGray">
    <TextBox Margin="5">
        <TextBox.Text>
            <Binding ElementName="slider1" Path="Value" Mode="OneWay"/>
        </TextBox.Text>
    </TextBox>
    <Slider x:Name="slider1" Margin="5"/>
</StackPanel>

​ 不是所有类都能使用标记扩展 ,只有MarkupExtension的派生类可以使用 他的派生类不多:

  • System.Window.ColorConvertedBitmapExtension
  • System.Window.Data.BindingBase
  • System.Window.Data.RelativeSource
  • System.Window.DynamicResourceExtension
  • System.Window.Markup.ArrayExtension
  • System.Window.Markup.NullExtension
  • System.Window.Markup.StaticExtension
  • System.Window.Markup.Type.Extension
  • System.Window.ResourceKey
  • System.Window.StaticResourceExtension
  • System.Window.TemplateBindingExtension
  • System.Window.ThemeDictionaryExtension

注意:

标记扩展是可以嵌套的,例如 Text="{Binding Source={StaticResource myDataSource},Path=PersonName}" 是正确的语法

标记有简写语法 如: {Binding Value ...} === {Binding Path=Value}

标记扩展类的类名以 Extension 结尾 带有的可以省略不写 如: Text="{x:Static...}"

3.3 事件处理器与代码后置

XAML标签的 Attribute 对应着 Property 但是有些对应着 对象事件 Event

事件处理器

WPF支持在XAML里面为对象的事件指定处理器, 方法是使用事件处理器的函数名为对应的对象事件的Attribute进行赋值:

<ClassName EventName="EventHandlerName"></ClassName>

例如按钮:

<Button Content="事件" Click="Button_Click"/> 

如果翻译成c#代码

Button btn = new Button();
btn.Click += Btn_Click;

代码后置的意思就是 逻辑和ui分开

补充:如果想在XAML中用c#代码 可以使用 x:Code 标签 但是 x:Code的内容一定要使用XML的<![CATA[....]]>

<Button Content="事件" Click="Button_Click"/>
<x:Code>
    <![CDATA[
                private void Button_Click(object sender,RoutedEventArgs e){
                    MessageBox.Show("asdfasdf");
                }
            ]]>
</x:Code>

3.4 导入程序集和引用其中的名称空间

把类库引用到项目中是引用其中名称空间的物理基础,无论是c#还是XAML都是这样

一旦引入成功,就能使用其名称空间

架设我们使用类库程序集为 MyLibray.dll 其中包括 Common和 Controls两个名称空间 那么XAML中引用语法是:

xmlns:映射前缀="clr-namespace=:名称空间,assembly=类库名"
xmlns:Common="clr-namespace=:Common,assembly=MyLibray"
xmlns:Controls="clr-namespace=:Controls,assembly=MyLibray"

一旦引入成功 就可以使用其语法

<映射名:类名></映射名:类名>
<Common:MessagePanel/>

c# 也可以像xaml一样引用 using Cmn = Common;

3.5 XAML的注释

<!---->
/*
总结:
	xaml 标签的两种给属性值的方式 一个是对象 一个是字符串 对象的时候需要在 父标签内容中写属性的标签
	扩展标记 {}   
	事件 EventName=""  例如 click="函数名"
	程序集的引用   xmlns:映射前缀="clr-namespace:名称空间,assembly=程序集" 前提先引入
	xaml的注释 <!---->
*/

4 x名称空间的详解

x字符可以随便改,只是我们修改的名称罢了 ,x名称空间里的成员(如 x:Class x:Name) 是专门给xaml编译器看的,用来引导xaml编译器把xaml代码转换为CLR代码

4.1 x名称空间里都有什么

x包含的均匀解释XAML语言相关,所以可以称之为XAML名称空间

x里面的参数可以告诉 编译器 编译结果与那个c#类结合,访问级别等

  • x:Array 扩展标签
  • x:Class Attribute
  • x:ClassModifier Attribute
  • x:Code XAML指令元素 上面有写<![CDATA][]>
  • x:FiledModifier
  • x:Key
  • x:Name
  • x:Null 标签扩展
  • x:Shared
  • x:Static 标签扩展
  • x:Subclass
  • x:Type 标签扩展
  • x:TypeArguments
  • x:Uid
  • x:XData XAML指令元素

我们可以看到 分为三大类: Atterbute 标签扩展 XAML指令元素 下面我们分别说下他们

4.2 x名称空间中Attribute

4.2.1 x:Class

这个Atttribute的作用是告诉XAML编译器将XAML标签的编译结果与后台代码中指定类合并,使用注意事项:

  • 必须在根节点
  • 根节点要与x:Class 的值 保持一致!!!!!!!!!!
  • x:Class 指向的类要加partial关键字

4.2.2 x:ClassModifier

这个Attribute的作用是告诉XAML编译由标签编译生成的类具有怎样的访问控制级别

注意:

  • 这个标签必须要有x:Class Attribute
  • x:ClassModifier 要与 x:Class指向的值访问权限一致

4.2.3 x:Name

x:Name的作用有两个:

  • 告诉XAML编译器,当一个标签带有 x:Name 时 除了为这个标签生成对应的实例之外,还要为这个实例声明一个引用变量,变量名就是 x:Name
  • 将XAML标签所对应对象的Name属性(如果有) 也设置为x:Name的值,并把这个值注册到UI树上,方便查找

当一个 标签有 Name(FrameworkElement)的时候 Name 和 x:Name 都是一样的

4.2.4 x:FieldModifier

用于改变 x:Name 对应的 访问级别

<StackPanel x:Name="stackPanel" x:FieldModifier="public" Background="LightSlateGray">

只能改变变量的访问级别,前提要有一个x:Name

4.2.5 x:Key

最自然的检索方式莫过于使用"Key-Value"对的形式了, 在XAML文件中,我们可以把很多需要多次使用的内容提取出来放在资源字典中(Resource Dictionary)里,需要这个资源的时候可以 用key提取出来

例如我们在资源字典添加一个字符串

<Window x:Class="_002_标记扩展.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:_002_标记扩展"
        mc:Ignorable="d"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <sys:String x:Key="myString">
            Hello WPF Resource!
        </sys:String>
    </Window.Resources>
    <StackPanel x:Name="stackPanel" x:FieldModifier="public" Background="LightSlateGray">
        <TextBlock Text="{StaticResource ResourceKey=myString}" Margin="5"/>
    </StackPanel>
</Window>

c# 也能拿到资源

private void Button_Click(object sender, RoutedEventArgs e)
{
    string str = this.FindResource("myString") as String;
    this.button.Content = str;
}

4.2.6 x:Shared

当我们把某些对象当做资源放进去 取出来的时候是对象本身还是对象副本呢

  • 需要与x:Key配合使用
  • 如果x:Shared 为 true 那么取出来的都是同一个对象
  • XAML会给资源隐藏添加x:Shared="true", 默认情况都是同一个对象

4.3 x名称空间中的标记扩展

4.3.1 x:Type

MyButton xaml

<Window x:Class="_002_标记扩展.MyWindow"
        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:_002_标记扩展"
        mc:Ignorable="d"
        Title="MyWindow" Height="170" Width="200">
    <StackPanel>
        <TextBox Margin="5"/>
        <TextBox Margin="5"/>
        <TextBox Margin="5"/>
        <Button Content="OK" Margin="5"/>
    </StackPanel>
</Window>

MyWindow

public class MyButton:Button{

    public Type userWindowType { get; set; }

    protected override void OnClick()
    {
        base.OnClick();
        Window win = Activator.CreateInstance(this.userWindowType) as Window;
        win?.ShowDialog();
    }
}

主窗口

<local:MyButton userWindowType="{x:Type TypeName=local:MyWindow}" Margin="5" Content="show2" Click="MyButton_Click"/>

x:Type 标记扩展具有与 C# 中的 typeof() 运算符或 Microsoft Visual Basic 中的 GetType 运算符类似的功能。

userWindowType 通过x:type 修改了 类型为 MyWindow 所以显示的也是MyWindow

然后点击click后处理代码 代码显示窗口... x:type 标记扩展获取数据类型

4.3.2 x:Null *

重点是 key的访问方式

在xaml中 用来表示空值的是x:Null

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
            <Setter Property="Width" Value="60"/>
            <Setter Property="Height" Value="36"/>
            <Setter Property="Margin" Value="5"/>
        </Style>
    </Window.Resources>
    <StackPanel>
        <Button Content="OK"/>
        <Button Content="OK"/>
        <Button Content="OK"/>
        <Button Content="OK" Style="{x:Null}"/>
    </StackPanel>
</Window>

<Button>
    <Button.Style>
    	<x:Null/>
    </Button.Style>
</Button>

4.3.4 x:Array

如果想在XAML文档里声明一个包含数据的X:array实例,必须使用标签声明才能做到

x:Array的作用就是通过他的Items属性向使用者暴露一个类型已知的 ArrayList实例,ArrayList内成员类型由: x:Array 的type指明

在WPF中把包含数据的对象称为 Data Source 如果想把 x:Array 当做一个数据向一个ListBox提供数据

<Window x:Class="WpfApp1.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:sys="clr-namespace:System;assembly=mscorlib" 
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>

        <ListBox Margin="5" Height="300">
            <ListBox.ItemsSource>
                <x:Array Type="sys:String">
                    <sys:String>hello</sys:String>
                    <sys:String>hello</sys:String>
                    <sys:String>hello</sys:String>
                    <sys:String>hello</sys:String>
                </x:Array>
            </ListBox.ItemsSource>
        </ListBox>
        
        
    </StackPanel>
</Window>

4.3.5 x:Static

他的功能是在xaml文档中使用数据类型的static成员

4.4 XAML指令元素

  • x:Code
  • x:XData

有一个类叫做 xmlDataProvider实例

<Window.Resources>
    <XmlDataProvider x:Key="InventoryData" XPath="?">
        <x:XData>
            <supermaket xmlns="">
                <Fruits>
                    ...
                    ..
                </Fruits>
            </supermaket>
        </x:XData>
    </XmlDataProvider>
</Window.Resources>

5 控件与布局

5.1 控件

日常我们打交道最多的控件无外乎6类,即

  • 布局控件 :可以容纳多个控件或嵌套其他布局控件,用于UI上组织和排列控件 Grid StackPanel DockPanel 都属于 他们都继承 Panel
  • 内容控件: 只能容纳一个控件 或 布局控件作为内容,Window Button 等控件属于此类,他们共同的父类是 ContentControl
  • 带标题内容控件: 相当于一个内容控件,但是可以加个标题 (Header),标题部分亦可容纳一个控件或布局. GroupBox TabItem 等这些都是,他们的父类是 HeaderedContentControl
  • 条目控件: 可以显示一列数据,一般情况下这列数据的类型相同, ListBox ComboBox等,他们的共同基类是 ItemsControl
  • 带标题条目控件: 相当一个条目控件上加了一个标题 TreeViewItem MenuItem 都属于此类控件,这个用于显示层级关系 节点显示在 Header区域 他们共同基类是: HeaderedItemsControl
  • 特殊内容控件: 比如TextBox 容纳的是字符串,TextBlock 可以容纳可自由控制格式的文本,Image容纳图片类型数据, 这些控件比较独立 ...

image-20220725092651160

5.2 WPF的内容模型

WPF UI元素可以分为: 11个

名称 注释
ContentControl 单一内容控件
HeaderedContentControl 带标题的单一内容控件
ItemsControl 以条目集合为内容的控件
HeaderedItemsControl 带标题的以条目集合为内容的控件
Decorator 控件装饰元素
Panel 面板类元素
Adorner 文字点缀元素
Flow Text 流式文本元素
TextBox 文本输入框
TextBlock 静态文字
Shape 图形元素

5.3 各类内容模型详解

我们把符合某内容模型的ui元素 称为一个族,每个族用他们共同基类来命名

5.3.1 ContentControl 族

本族元素的特点如下:

  • 均派生自ContentControl
  • 他们都是控件 Control
  • 内容属性的名称为 content
  • 只能由单一元素充其当内容

如何理解 只能由单一元素充其当内容 这句话? 就是内容只能有一个 ..... 但是内容也可以是控件...

ContentControl族控件包括

1 1 1 1
Button ButtonBase CheckBox ComboBox
ContentControl Frame GridViewColumnHeader GroupItem
Label ListBoxItem ListViewItem NavigationWindow
RadioButton RepeatButton ScrollViewer StatusBarItem
ToggleButton ToolTip UserControl Window

5.3.2 HeaderedContentControl族

本族元素特点如下:

  • 他们都派生自 HeaderedContentControl 类,HeaderedContentControlContentControl的派生类
  • 他们都是控件 用于显示带标题的数据
  • 除了主区域,还有一个显示标题(Header) 的区域
  • 内容属性为 ContentHeader 都只能容纳一个元素..
Expander GroupBox HeaderedContentControl TabItem
<GroupBox Margin="10" BorderBrush="Gray" Height="80" Width="200">
    <GroupBox.Header>
        <Image Source="C:\Users\DSF-LSJ\Pictures\logo.ico" Width="20" Height="20"/>

    </GroupBox.Header>

    <TextBlock TextWrapping="WrapWithOverflow" Margin="10" Text="床前明月光,疑是地上霜,举头望明月,低头思故乡"/>
</GroupBox>

5.3.3 ItemsControl族

本族元素特点如下:

  • 都派生自 ItemsControl类
  • 他们都是控件,用于显示列表化的数据
  • 内容 属性为 ItemsItemsSource
  • 每种ItemsControl 都对应有自己的条目容器 (Item Container)
1 1 1 1
Menu MenuBase ContextMenu ComboBox
ItemsControl ListBox ListView TabControl
TreeView Selector StatusBar
<ListBox Margin="5">
    <CheckBox Content="Time"/>
    <CheckBox Content="Time"/>
    <CheckBox Content="Time"/>
</ListBox>

书70页有写 怎么获取ui树的

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        List<student> newList = new List<student>()
        {
            new student(){id=1,name="hello1"},
            new student(){id=2,name="hello2"},
            new student(){id=3,name="hello3"},
            new student(){id=4,name="hello4"}
        };

        this.testListBox.DisplayMemberPath = "name";
        this.testListBox.SelectedValuePath = "id";
        this.testListBox.ItemsSource = newList;
    }
}

public class student
{
    public int id { get; set; }
    public string name { get; set; }
}
 <ListBox x:Name="testListBox"/>

DisplayMemberPath 告诉 listbox显示那条属性

SelectedValuePath listbox.SelectValue 的时候显示

理解了ListBox的自动包装机制后 我把全部ItemsControl 对应的Item Contatiner列在下面

ItemsControl名称 对应的 item Container
ComboBox ComboBoxItem
ContenxtMenu MenuItem
ListBox ListBoxItem
ListView ListViewItem
Menu MenuItem
StatusBar StatusBarItem
TabControl TabItem
TreeView TreeViewItem

5.3.4 HeaderedItemsControl 族

顾名思义,都有itemsControl的特性 但是还可以有标题

只是多个一个 Header属性

MenuItem TreeViewItem ToolBar
MenuItem TreeViewItem ToolBar

5.3.5 Decorator 族

本族中的元素是在UI上起装饰效果,如 可以使用border元素为一些组织在一起的内容添加个边框,如果需要组织在一起的内容能够自由缩放,则可以使用ViewBox元素

  • 均为Decorator类派生
  • 起UI装饰效果
  • 内容属性为Child
  • 只能由单一元素充当内容
ButtonChrome ClassicBorderDecorator ListBoxChrome SystemDropShadowChrome
Border InkPresenter BulletDecorator ViewBox
AdornerDecorator

5.3.6 TextBlock 和 TextBox

TextBlock只能显示文本 不能编辑,所以叫做静态文本,

TextBox 允许用户编辑

5.3.7 Shape族元素

这类元素没有自己的内容,我们可以使用Fill属性为他们的填充效果,还可以使用Stroke 属性设置他们的边界线效果

  • 均为 Shape类
  • 用于2D图形绘制
  • 无内容属性
  • 使用Fill 属性设置填充 ,使用 Stroke设置边线

5.3.8 Panel族元素

  • 均派生自Panel 类
  • 主要功能就是控制UI布局
  • 内容属性为Children
  • 内容可以多个 Panel控制他们布局
Canvas DockPanel Grid TabPanel
ToolBarOverflowPanel StackPanel ToolBarPanel UniformGrid
VirtualizingPanel VirtualizingStackPanel WrapPanel

..

5.4 UI布局(Layout)

WPF布局元素有一下几个:

  • Grid 网格,可以自定义行和列并通过行列的数据,行高列宽控制布局 类似 html的table
  • StackPanel:栈式面板 可以排成水平或垂直的一条线 当移除一个元素后,后面的元素会自动向前填充空缺
  • Canvas: 画布 内部元素可以使用像素为单位的绝对坐标进行定位
  • DockPanel:泊靠式面板,内部元素可以选择泊靠方向
  • WrapPanel: 自动折行面板, 内部在排满一行后就能自动换行,类似html的流式布局

5.4.2 Grid

  • 可以自定义任意数量行和列
  • 内部元素可以选择在什么位置
  • 可以设置 children 元素的对齐方式

使用场合:

  • UI布局的大框架设计
  • 大量ui元素需要排成行或列的情况
  • UI整体尺寸改变时,元素需要保持固有的高度和比例等
  • UI后期可能有较大的变更和扩展
  1. 定义Grid的行与列

    Grid 类具有 ColumnDefinitions 和 RowDefinitions 两个属性 他们分别是 ColumnDefinition 和 RowDefinition 的集合,表示了Grid定义了多少列和多少行

    两行三列:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
    </Grid>
    

    设置 行高 和 列宽

    单位:

    英文名称 中文名称 简写 换算
    Pixel 像素 px 默认单位 图像基本单位
    Inch 英寸 in 1inch = 96pixel
    Centimeter 厘米 cm 1cm=(96/2.54)pixel
    Point pt 1pt=(96/72)pixel
    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="50px"/>
            <RowDefinition Height="50px"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="40"/>
            <ColumnDefinition Width="1in"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
    </Grid>
    

    他的值我们可以设置三类

    • 绝对值 数值后面加单位
    • 比例值 数值后面加*****号 (他会把所有值加起来当分母 , 然后当个值做分子)
    • 自动类型 填写 Auto字符串
  2. 使用Grid进行布局

    <Window x:Class="_005_布局.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:_005_布局"
            mc:Ignorable="d"
            Title="MainWindow" Height="400" Width="400" >
        <Grid ShowGridLines="False" Margin="10" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" MinWidth="120"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="80"/>
                <ColumnDefinition Width="4"/>
                <ColumnDefinition Width="80"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="25"/>
                <RowDefinition Height="4"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="4"/>
                <RowDefinition Height="25"/>
                
            </Grid.RowDefinitions>
    
            <TextBlock Text="请选择您的部门并留言:" Grid.Column="0" Grid.Row="0" VerticalAlignment="Center"/>
            <ComboBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4"/>
            <TextBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="5" BorderBrush="Black"/>
            <Button Content="提交" Grid.Row="4" Grid.Column="2"/>
            <Button Content="取消" Grid.Row="4" Grid.Column="4"/>
        </Grid>
    </Window>
    
    

5.4.3 StackPanel

  • 同类元素需要紧凑排列(如制作菜单或者列表)
  • 移除其中的元素后能够自动不缺的布局

三个属性:

属性名称 数据类型 可取值 描述
Orientation Orientation枚举 Horizontal
Vertical
决定内部元素是横向累计还是纵向累积
HorizontalAlignment HorizontalAlignment枚举 Left
Center
Right
Stretch
决定内部元素水平方向向上对齐方式
VerticalAlignment VerticalAlignment枚举 Top
Center
Bottom
Stretch
决定垂直方向的对齐方式
<GroupBox Header="test" BorderBrush="Black" Margin="5">
    <StackPanel>
        <CheckBox Content="hello1"/>
        <CheckBox Content="hello1"/>
        <CheckBox Content="hello1"/>
        <CheckBox Content="hello1"/>
        <CheckBox Content="hello1"/>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Content="确定" Width="60" Margin="5"/>
            <Button Content="取消" Width="60" Margin="5"/>
        </StackPanel>
    </StackPanel>
</GroupBox>

5.4.4 Canvas

他有 Canvas.XCnavas.Y属性

用处:

  • 一经设计基本上不会再有改动的小型布局(如图标)
  • 艺术性比较强的布局
  • 需要大量使用横纵坐标进行绝对定位的布局
  • 依赖于横纵坐标的动画
<Canvas HorizontalAlignment="Left" Height="270" Margin="36,47,0,0" VerticalAlignment="Top" Width="300">
    <TextBlock Canvas.Left="142" TextWrapping="Wrap" Text="TextBlock" Canvas.Top="100"/>
    <StackPanel Height="100" Canvas.Left="38" Canvas.Top="160" Width="100"/>
    <Label Content="Label" Canvas.Left="56" Canvas.Top="52"/>
</Canvas>

5.4.5 DockPanel

DockPanel内的 元素 会附加上 DockPanel.Dock 这个属性 这个属性的数据类型是 枚举 Left Top Right 和 Bottom ,根据Dock的值,DockPanel内的元素会向指定方向累积,切分DockPanel内部的剩余可用空间,就像船舶靠岸一样

DockPanel还有一个重要属性---bool 类型的 LastChideFill 他默认是 True,当他为True的时候 DockPanel的最后一个元素 DockPanel.Dock 属性会被忽略,这个元素会把剩下的空间填充满

<DockPanel >
    <TextBox BorderBrush="Black" Height="25" Text="123" DockPanel.Dock="Top"/>
    <TextBox BorderBrush="Black" Width="25" Text="123" DockPanel.Dock="Left"/>
    <TextBox BorderBrush="Black"/>
</DockPanel>
<Grid ShowGridLines="False" Margin="10" >
    <Grid.RowDefinitions>
        <RowDefinition Height="25"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="150"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <TextBox Grid.ColumnSpan="3" BorderBrush="Black"/>
    <TextBox Grid.Row="1" BorderBrush="Black"/>

    <GridSplitter Grid.Row="1" Grid.Column="1"
                  VerticalAlignment="Stretch" HorizontalAlignment="Center"
                  Width="5" Background="Gray" 
                  ShowsPreview="True"/>

    <TextBox Grid.Row="1" Grid.Column="2" BorderBrush="Black"/>

</Grid>

5.4.6 WrapPanel

WrapPanel 内部采用的是流式布局, WrapPanel使用Orientation属性来控制流的延伸方向,使用 HorizontalAlignment和VerticalAlignment两个属性控制内部控件的对齐

在流延伸的方向上,WrapPanel 会排列尽可能多的控件,排不下的会新起一排继续排列

<WrapPanel>
    <Button Width="50" Height="50" Content="OK"/>
    <Button Width="50" Height="50" Content="OK"/>
    <Button Width="50" Height="50" Content="OK"/>
</WrapPanel>
posted @ 2022-07-26 09:19  LD_Dragon  阅读(260)  评论(0编辑  收藏  举报