一、XAML基础语法
1.xaml语言简介
XAML 是一种声明性标记语言,应用于 .NET 编程模型时,XAML 简化了为 .NET 应用创建 UI
以文本形式表示时,XAML 文件是通常具有扩展名的 .xaml 文件
可通过任何XML 编码进行编码,但编码通常为UTF-8格式
2.对象(Object)元素
对象元素,通过声明 XML 元素来实例化 CLR 类或结构
XAML 例如 <Button>标签,其实是在说 "帮我创建一个 C# 类的对象"
用标签名当类名, <Button> 可以看作是 new Button()
而标签内部写的内容,实际上是在给对象设置属性
XAML 标签(对象元素)就是 C# 对象;标签属性就是 C# 属性;标签正文是内容属性
对象元素语法有两种:
1.自闭合 => 控件无内容
以左尖括号(<)开始,中间写属性,然后以正斜杠紧跟右尖括号结束(/>)
<Button Name="CheckoutButton"/>
2.成对标签 => 控件有内容
以<组件>开始,中间写内容属性,以</组件>结束
<TextBox>
我是 Text 内容属性
</TextBox>
看了微软官方的语法详讲,它叽里咕噜的在说什么呢
3.属性
语法:
<控件 属性名="属性值" />
<!-- —————————————————————————————————————————————————————————————————————————————————————————— -->
<!-- 示例 -->
<Button Name="CheckoutButton"/>
别管微软官方文档那一堆叽里咕噜的话的,就这么简单
4.属性(Property)元素
属性元素以<控件.属性名>开始,</控件.属性名>结束,中间内容是属性值所对应类型的对象元素
语法示例:
<控件>
<控件.属性名>
<!-- 对象元素 -->
<属性类型 属性名="属性值"/>
</控件.属性名>
</控件>
<!-- —————————————————————————————————————————————————————————————————————————————————————————— -->
<!-- 示例 -->
<Button>
<Button.Background>
<SolidColorBrush Color="Red"/>
</Button.Background>
<!-- Content => 这个内容属性指按钮里面放的东西,可以是文字,也可以是图片等-->
<Button.Content>
这是一个按钮
</Button.Content>
</Button>
5.集合
集合属性 = 如果类型是 IList/IDictionary/Array,子元素就是集合项
XAML 在加载时评估每项为对象并隐式调用 Add 来加入集合
如果要显式写集合对象,集合类必须有无参构造函数
显式集合语法:写出集合本身
隐式集合语法:不写集合,直接写集合的子项
别管什么隐式显式了,在多看一眼我就要大隐隐于市了(语文老师:这成语我是这样教你用的?)
语法:
<!-- 隐式集合 => 属性名被我吃了(不写集合对象本身,系统自动创建) -->
<控件>
<集合元素 /> <!-- 自动加入控件的某个集合属性 -->
<集合元素 />
</控件>
<!-- —————————————————————————————————————————————————————————————————————————————————————————— -->
<!-- 示例 -->
<StackPanel>
<Button Content="按钮1" />
<Button Content="按钮2" />
</StackPanel>
<!-- 显式集合 => 明确写出属性名(写集合对象本身) -->
<控件>
<控件.集合属性名>
<集合类型> <!-- 显式写出集合对象本身 -->
<集合元素 />
<集合元素 />
</集合类型>
</控件.集合属性名>
</控件>
<!-- —————————————————————————————————————————————————————————————————————————————————————————— -->
<!-- 示例 -->
<!-- Polyline => 用三个坐标点画一条折线 -->
<Polyline Stroke="Black" StrokeThickness="2">
<Polyline.Points>
<PointCollection>
<Point X="0" Y="0" />
<Point X="50" Y="20" />
<Point X="100" Y="0" />
</PointCollection>
</Polyline.Points>
</Polyline>
6.内容属性
内容属性 = "默认接收子元素或文本的属性"
内容属性只能有一个且内容必须是连续的(不能中间夹其他属性元素)
<!-- ✅ -->
<Button>
I am a blue button
</Button>
<!-- ❌ -->
<Button>
I am a
<Button.Background>
Blue
</Button.Background>
blue button
</Button>
示例:
<!--Border 的内容属性是 Child-->
<!-- 显式写法 -->
<Border>
<Border.Child>
<TextBox Width="300"/>
</Border.Child>
</Border>
<!-- 隐式写法 -->
<Border>
<TextBox Width="300"/>
</Border>
<!-- —————————————————————————————————————————————————————————————————————————————————————————— -->
<!-- Button 的内容属性是 Content -->
<!-- 显式写法 -->
<Button>
<Button.Content>
点击我
</Button.Content>
</Button>
<!-- 隐式写法 -->
<Button>
点击我
</Button>
7.根元素 和 命名空间
根元素:XAML 文件中最外层的元素,就是整个 XAML 的根
XAML 文件必须有一个根元素,WPF中通常使用Window,Application,Page等作为根
根元素下面的元素都是根元素的子元素
需要特别注意:每个 XAML 文件有且只能有一个根元素
<!-- 这个XAML文件的根就是Window -->
<!-- Grid和Button都是根元素Window的子元素 -->
<Window x:Class="WPF_Test.MainWindow"
......
<Grid>
<Button Content="点击我"/>
</Grid>
</Window>
命名空间:用来告诉 XAML 解析器 元素对应哪个 CLR 类库(指明控件所属类库或项目)
根元素还包含属性xmlns,而xmlns命名空间的类型定义
需要注意的是:xmlns="......"中的内容并不是网址
而是由微软官方定义的看起来有一点像网址的命名空间标识符(Namespace URI)
代码示例:
# 表示默认使用的命名空间
# 将整个 WPF 客户端/框架 XAML 命名空间映射为默认值
xmlns="......"
# 表示命名空间 mc
# 表示将单独的 XAML 命名空间映射到 mc
xmlns:mc="......"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WPF_Test"
# 前者表示系统自带的命名空间 -> 对比C语言 #include <stdio>
# 后者表示自定义命名空间 -> 对比C语言 #include "MyCode"
<Window x:Class="WPF_Test.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:Icon="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
xmlns:uc="clr-namespace:DataMonitoring.UserControls"
xmlns:local="clr-namespace:WPF_Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Polyline Stroke="Black" StrokeThickness="2">
<Polyline.Points>
<PointCollection>
<Point X="100" Y="100" />
<Point X="50" Y="20" />
<Point X="100" Y="10" />
</PointCollection>
</Polyline.Points>
</Polyline>
</Grid>
</Window>
8.类型转换器(TypeConverter)
微软官方:它指出属性值必须由字符串设置,字符串转换为其他对象类型或基元值的基本处理方式基于String类型本身,以及特定类型的本机处理 请求中译中
一个类或机制,用来把 XAML 文本值 转换成 CLR 对象
当你在 XAML 中写一个属性值(通常是字符串),XAML 解析器需要把它转换成控件的属性类型
TypeConverter = XAML 的翻译器,把字符串翻译成实际对象
示例:
最常见的就是设置颜色或者边距
编译器可不知道你的什么Red,什么数字是什么意思
只有通过类型转换器,它才可以知道是什么意思
<Button Background="Red" Content="点我"/>
<Button Margin="10,5,10,5" Content="按钮"/>
省流:
类型转换器就是翻译器,把你在xaml文件中写的字符串翻译成对象
如果没有转换器,XAML 就不能直接把 "Red" 或 "10,5,10,5" 放进属性里
9.事件(Event)
C#中事件是什么样子的,那么WPF中的事件就是什么样子的,我想,应该也没有人会不看C#基础语法先来看WPF吧
事件 = 控件告诉你 "我被()了",你执行事件函数 XX
在XAML中通常用 Click = "事件函数名"表示
示例代码:当你点击了按钮,触发点击事件Button_Click
<Button Content="点我" Click="Button_Click"/>
// 如果在XAML文件中写了事件,C#代码中会自动生成对应事件函数,然后逻辑自己写
private void Button_Click(object sender, RoutedEventArgs e)
{
// 业务逻辑
}
10.标记扩展
标记扩展主要用于简化语法,以不同于一般将属性值作为文本字符串或字符串可转换值处理的方式来处理它
{标记扩展 ...}
<!-- 标记扩展 -->
<TextBlock Text="{Binding Age}" />
<!-- 如果没有标记扩展 -->
<TextBlock>
<TextBlock.Text>
<Binding Path="Age"/>
</TextBlock.Text>
</TextBlock>
标记扩展 用途 示例
{Binding ...} 绑定数据(数据绑定表达式) Text="{Binding UserName}"
{StaticResource ...} 查找静态资源 Background="{StaticResource BlueBrush}"
{DynamicResource ...} 动态查找资源 Background="{DynamicResource ThemeColor}"
{x:Static ...} 引用常量或静态属性 Text="{x:Static local:Config.AppName}"
{x:Type ...} 获取 Type 类型 Tag="{x:Type Button}"
{TemplateBinding ...} 模板绑定(控件模板内部) Text="{TemplateBinding Content}"
11.附加属性
附加属性:某个类定义的属性,可以被其他完全无关的控件来使用
给子控件加额外信息,让父控件来 读取 / 处理
<!-- Grid.Row和Grid.Column 都不是 Button 的属性 -->
<Grid>
<!-- Row代表行,Column代表列,这里指将按钮放在第1行第2列 -->
<Button Content="点我" Grid.Row="1" Grid.Column="2"/>
</Grid>
二、基础控件
这部分主要使用代码演示,文档?文档叽里咕噜不知道在说什么,拿头看,不如直接代码演示
1.窗口常用属性
(1)标题Title,高度Height,宽度Width
# Window表示当前窗口根元素,也可以理解为当前窗口就是Window
# Title -> 窗口标题(图标名)
# Height -> 高度
# Width -> 宽度
Title="MainWindow" Height="450" Width="800">
(2)窗口初始位置 WindowStartupLocation
# 窗口初始位置位于 屏幕中央
WindowStartupLocation="CenterScreen"
# 窗口初始位置位于 所有者(需要手动设置)中间位置
# 所有者:这个解释起来有点抽象,你可以理解为:
# 一个主窗口,你点击了一个按钮,跳出来一个新窗口
# 你将主窗口设置为了新窗口的所有者,那么新窗口初始位置就是主窗口的中间位置
WindowStartupLocation="CenterOwner"
# 窗口初始位置位于坐标(100, 300)位置处 -> 左上角为原点
WindowStartupLocation="Manual" Top="100" Left="300"
(3)窗口图标修改 —— Icon
1)先添加图片文件到项目目录下,再右击图片选择属性,在 "高级→生成操作中选择Resourse(资源)"
部分版本Resourse直接翻译为"资源"选项
WPF
image-20251210161801488
2)在对应位置设置该图片为图标图片
# 这里使用的是相对文件路径
Icon="/Images/1.jpg"
(4)应用程序图标(.exe可执行文件图标)修改
1)右击项目,选择属性
2)选择 "应用程序 → 资源 → 图标和清单"
特别注意,图标只能是.ico文件
image-20251210163801637
(5)整体代码示例:
<Window x:Class="WPF_Test.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:WPF_Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
WindowStartupLocation="CenterScreen">
<Grid>
</Grid>
</Window>
(6)其他常用的一些窗口属性
属性 类型 作用
Title string 窗口标题
Width / Height double 窗口宽高
MinWidth / MinHeight double 最小尺寸
MaxWidth / MaxHeight double 最大尺寸
WindowStartupLocation enum 窗口初次显示位置:Manual / CenterScreen / CenterOwner
ResizeMode enum 是否可调整大小:CanResize / CanMinimize / NoResize
WindowStyle enum 边框样式:SingleBorderWindow、None(无边框)等
Top / Left double 窗口在屏幕的位置(Manual 模式下才生效)
WindowState enum 普通、最大化、最小化:Normal / Maximized / Minimized
ShowInTaskbar bool 是否显示在任务栏
Topmost bool 窗口是否置顶(所有窗口的顶层)
Icon ImageSource 窗口图标(支持相对路径)
Background Brush 背景色
AllowsTransparency bool 是否允许透明(WindowStyle=None 时常用)
Opacity double 窗口透明度(0~1)
Owner Window 设置当前窗口的拥有者,用于子窗口位置与模式对话框
Content UIElement Window 的内容(通常是 Grid 或其他布局控件)
2.Grid
Grid 是 WPF 最强大的布局控件
Grid用行和列把界面分成格子,然后把控件放进这些格子里面
你甚至可以在Grid控件里面放Grid
(1)行和列
行和列都是默认从0开始计数的
组件未声明行列,默认第0行第0列
<Grid>
<!-- 定义行 RowDefinitions -->
<!-- 这里有3行 -->
<Grid.RowDefinitions>
<RowDefinition Height="50" /> <!-- 🌱固定值 => 50px -->
<RowDefinition Height="*" /> <!-- 🌱按比例分配剩余空间 => 1/3 -->
<RowDefinition Height="2*" /> <!-- 占剩余空间的2/3 -->
</Grid.RowDefinitions>
<!-- 定义列 ColumnDefinitions -->
<!-- 这里有3列 -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" /> <!-- 固定值 100px -->
<ColumnDefinition Width="Auto" /> <!-- 🌱根据内容自动 -->
<ColumnDefinition Width="*" /> <!-- 占全部剩余空间 -->
</Grid.ColumnDefinitions>
<Button Content="我不是按钮"/> <!-- 🌱默认第0行0列 -->
<Button Content="我是按钮"/> <!-- 🌱依旧默认第0行0列 -->
<Button Content="AAA" Grid.Row="1" Grid.Column="1"/> <!-- 🌱第1行1列 -->
</Grid>
(2)边框
ShowGridLines,bool类型,默认为False
<Grid ShowGridLines="True">
</Grid>
(3)合并行、列
Grid.RowSpan:合并行(部分资料翻译为:跨行,指单元格可以跨行进行多行操作)
Grid.ColumnSpan:跨列合并列(部分资料翻译为:跨列,指单元格可以跨列进行多列操作)
示例代码:
<!-- 将前2行和前3列合并到一个单元格中 -->
<Button Content="我不是按钮" Grid.ColumnSpan="2" Grid.RowSpan="3"/>
(4)分割布局 GridSplitter
GridSplitter:使用分隔线用于分割布局
代码示例:
在一个Grid布局中创建3列,第1列和第3列作为内容列,第二列作为分割线
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<!-- 分隔线 -->
<!-- ShowsPreview => 拖动分隔线时,是否出现预览状态,默认False -->
<GridSplitter ShowsPreview="True" Grid.Column="1" HorizontalAlignment="Center" Width="5"/>
<!-- TextBlock => 文本框; TextWrapping="Wrap" => 表示文本内容可自动换行 -->
<TextBlock Text="我是第一行" FontSize="20" TextWrapping="Wrap" Grid.Column="0"/>
<TextBlock Text="别听它的鬼话" FontSize="20" TextWrapping="Wrap" Grid.Column="2"/>
</Grid>
3.StackPanel
StackPanel = 堆叠布局容器
把子控件按照 水平 或 垂直 顺序,一个接一个摆放
1.默认 垂直布局 Vertical
2.可以定义高度和宽度
示例代码:
<Grid>
<!-- 默认垂直布局 => Orientation="Vertical" -->
<!-- 水平布局 => Orientation="Horizontal" -->
<StackPanel Orientation="Horizontal">
<Button Content="按钮A"/>
<Button Content="阿牛"/>
<TextBlock Text="AAA" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
4.控件外边距Margin
Margin = 控件外边距
外边距:控件与"外部其他东西"之间的空隙
代码示例:
# 表示 上下左右 外边距均为100
# 🌱Margin="上下左右"
Margin="100"
# 表示 左、右外边距均为22 - 上、下外边距为33
# 🌱Margin="左右 上下"
Margin="22 33"
# 表示 左外边距为22 上外边距为33 右外边距均为22 下外边距为33
# 🌱Margin="左 上 右 下"
Margin="22 66 99 33"
<Grid>
<StackPanel Orientation="Vertical">
<Button Content="按钮A"/>
<!-- Margin的4个数值对应的是左/上/右/下 外边距(顺时针) -->
<Button Content="阿牛" Margin="10 10 10 10"/>
<TextBlock Text="AAA" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
5.Border
Border:翻译过来是边界的意思,但是我更喜欢叫这个东西为 壳
Border = 给一个内容包上一层 边框 + 背景 的容器控件
盒子的外壳 → Border
盒子里面装的东西 → Child(内容属性)
[!IMPORTANT]
🌱特别注意:
Border中只能有一个控件,
但是你可以添加面板控件(StackPanel / Grid / DockPanel),在其中存放多个控件
间接实现Border拥有多个控件
Border
└── StackPanel (作为 Border 的唯一 Child)
├── TextBlock
├── Button
└── Image
(1)Border 的常用属性
属性 作用 示例
BorderBrush 边框颜色 BorderBrush="Black"
BorderThickness 边框粗细 BorderThickness="2"
Background 背景色 Background="LightGray"
CornerRadius 圆角 CornerRadius="10"
Padding 内边距 Padding="10"
代码示例:
<Grid>
<StackPanel Orientation="Vertical">
<Border BorderBrush="LightSteelBlue"
BorderThickness="2"
CornerRadius="15"
Padding="8">
<Button Content="这是一个有圆角的按钮" />
</Border>
<Button Content="阿牛" Margin="10 10 10 10" />
<Border BorderBrush="Black"
BorderThickness="1"
Background="LightYellow"
Padding="10">
<TextBlock Text="这是一个有边框的文本" />
</Border>
</StackPanel>
</Grid>
(2)使用Border绘画分隔线
<Window x:Class="WPF_Test.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:WPF_Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
WindowStartupLocation="CenterScreen"
Icon="/Images/1.jpg">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 横竖两条分隔线 -->
<Border Grid.RowSpan="3" Grid.Column="1" BorderBrush="Coral" BorderThickness="1 0 0 0"/>
<Border Grid.Row="2" Grid.ColumnSpan="3" BorderBrush="Coral" BorderThickness="0 1 0 0"/>
</Grid>
</Window>