一、 WPF 相关知识概述
1. 什么是 WPF 框架?
WPF 的全称是 Windows Presentation Foundation,中文通常翻译为“Windows 呈现基础”。它是一个由微软开发的、用于构建桌面应用程序的用户界面(UI)框架,是 .NET Framework(现在也兼容 .NET Core / .NET)的一部分。
2. 命名空间
a. 基本 WPF 命名空间
WPF 核心控件(如 Window, Grid, Button, TextBox 等)所在的命名空间。
b. XAML 语言命名空间(x:)
XAML 本身的一些特殊指令和属性(如 x:Name, x:Key, x:Class, x:Static, x:Bind 等)所在的命名空间。
<Window x:Class="MyWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="800">
<!-- 这里的 Grid 和 Button 默认就属于 http://schemas.microsoft.com/winfx/2006/xaml/presentation 命名空间 -->
<Grid>
<Button Content="点击我"/>
<Button x:Name="myButton" Content="点击我"/> <!-- 这里的 x:Name 就属于 x: 命名空间 -->
</Grid>
</Window>
c. 自定义(CLR)命名空间 / 映射的命名空间
想要在 XAML 中使用你自己的 C# 类(例如自定义控件、Converter、ViewModel 等),或者第三方库中的类时,就需要声明的命名空间。
3. 映射
“映射”在 WPF/XAML 中特指将一个XAML 前缀与一个CLR 命名空间和一个程序集(Assembly) 关联起来的过程。
a. 为什么需要映射
XAML 文件本身并不知道你的 MyCustomControl 类在哪里。它只知道 XML 元素名。为了让 XAML 处理器能够找到并实例化你的 C# 类,你必须告诉它:
这个 XAML 前缀(比如 local:)代表的是哪个 XAML 命名空间。
这个 XAML 命名空间实际上对应着 C# 代码中的哪个 namespace。
这个 C# namespace 又位于哪一个编译后的 DLL 文件(程序集)中。
映射的语法
xmlns:prefix="clr-namespace:ClrNamespaceName;assembly=AssemblyName"
b. 将 CLR 命名空间映射到程序集中的 xml 命名空间
[assembly: XmlnsDefinition(URI, CLRNamespace)]
在 AssemblyInfo.cs 中添加
二、 运行剖析
1. XAML 命名空间 x: 前缀
x: 前缀功能含义 (What it means) 作用 (What it does) x:Class 指示 XAML 元素(通常是根元素)关联的 CLR 类。 将 XAML 定义的 UI 结构与指定的 C#(或VB.NET)后台代码文件关联起来。 允许你在后台代码中访问 XAML 中定义的 UI 元素,并处理事件、实现业务逻辑。在编译时,XAML 会被处理成与此 CLR 类合并的部分类。x:Name 为 XAML 中声明的元素(如控件、面板)指定一个唯一的名称。 在运行时,允许你在 C# 后台代码中通过这个名称直接访问和引用 XAML 中声明的 UI 元素。 这类似于 HTML 中的 id 属性,是 UI 元素与代码交互的桥梁,例如:myTextBox.Text = "新的文本";。x:Key 为资源字典中的资源项指定一个唯一的键。 用于在 ResourceDictionary 中唯一标识一个资源。 其他 XAML 元素可以通过 StaticResource 或 DynamicResource 标记扩展,引用这个键来重用样式、模板、颜色刷、字符串等定义好的对象。例如:<Button Style="{StaticResource MyButtonStyle}"/>。
三、 常用控件(一)
1. 标签 Label
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Content object 设置标签显示的文本或其他任何 UI 元素内容。 Target UIElement 指定与此标签关联的 UI 元素,通常是输入控件(如 TextBox)。点击 Label 或使用其助记符时,焦点会转移到 Target 控件上。 VerticalAlignment VerticalAlignment 控制标签内容在标签内部可用空间的垂直对齐方式(Top, Center, Bottom, Stretch)。 HorizontalAlignment HorizontalAlignment 控制标签内容在标签内部可用空间的水平对齐方式(Left, Center, Right, Stretch)。
代码示例:
<Label Target="{Binding ElementName=emailTextBox}" Margin="0,10,0,0">
<AccessText Text="_电子邮件地址:"/>
</Label>
<TextBox x:Name="emailTextBox" Width="200" HorizontalAlignment="Left"/>
2. 文本显示 TextBlock
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Text string 获取或设置要显示的文本内容。 TextWrapping TextWrapping 定义文本是否在控件边界处自动换行(NoWrap, Wrap, WrapWithOverflow)。 TextTrimming TextTrimming 定义文本如何被截断以适应可用区域,当不换行时使用(None, CharacterEllipsis, WordEllipsis)。 FontFamily FontFamily 设置文本的字体族。 FontSize double 设置文本的字号,单位是设备无关像素。 FontWeight FontWeight 设置文本的粗细(如 Normal, Bold, Light 等)。 Foreground Brush 设置文本的颜色。 Background Brush 设置 TextBlock 控件的背景色或背景图片。
<StackPanel Margin="10">
<TextBlock Text="这是一个简单的文本显示。" FontSize="14" Foreground="DarkBlue"/>
<TextBlock Margin="0,10,0,0" TextWrapping="Wrap" TextTrimming="CharacterEllipsis">
这是一段很长的文本,当空间不足时,它会自动换行。如果 TextTrimming 设置为 CharacterEllipsis,则会在文本末尾显示省略号。
</TextBlock>
<TextBlock Margin="0,10,0,0">
混合样式: <Bold>粗体</Bold>, <Italic>斜体</Italic>, <Run Foreground="Red">红色</Run>。
</TextBlock>
</StackPanel>
3. 编辑框 TextBox
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Textstring获取或设置文本框当前显示的文本内容。用户可以输入和编辑此属性的值。 IsReadOnlybool设置文本框是否只读(true)或可编辑(false)。只读时用户仍可选择和复制文本。 MaxLengthint限制用户在文本框中可以输入的字符的最大数量。 AcceptsReturnbool控制用户按下 Enter 键时,是否插入换行符(true)或执行默认行为(如提交)。与 TextWrapping="Wrap" 结合实现多行输入。 AcceptsTabbool控制用户按下 Tab 键时,是否插入制表符(true)或将焦点移到下一个控件。 TextWrappingTextWrapping控制文本是否在宽度不足时自动换行(Wrap)。 VerticalScrollBarVisibilityScrollBarVisibility控制当内容超出文本框可见区域时垂直滚动条的显示行为(Auto, Disabled, Hidden, Visible)。 HorizontalScrollBarVisibilityScrollBarVisibility控制当内容超出文本框可见区域时水平滚动条的显示行为。
<StackPanel Margin="10">
<TextBox x:Name="singleLineTextBox"
Text="请在这里输入一行短文本。"
MaxLength="50" Width="250" HorizontalAlignment="Left"/>
<TextBox x:Name="multiLineTextBox"
Text="这是一个多行文本框。
请随意输入多行内容。"
AcceptsReturn="True" TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
Height="80"
Margin="0,10,0,0" Width="250" HorizontalAlignment="Left"/>
<TextBox Text="这是一个只读文本框。" IsReadOnly="True"
Background="LightGray"
Margin="0,10,0,0" Width="250" HorizontalAlignment="Left"/>
</StackPanel>
4. 按钮 Button
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Contentobject设置按钮上显示的文本、图像或其他 UI 元素内容。 IsEnabledbool控制按钮是否可用(true)或禁用(false)。禁用时按钮通常显示为灰色且不可交互。 CommandICommand绑定一个命令接口,当按钮被点击时,此命令将执行。常用于 MVVM 模式。 CommandParameterobject在按钮 Command 执行时,作为参数传递给命令的数据。 IsDefaultbool当设置为 true 时,此按钮在窗口中成为默认按钮。用户按下 Enter 键时若无其他活跃焦点,此按钮会被“点击”。 IsCancelbool当设置为 true 时,此按钮在窗口中成为取消按钮。用户按下 Esc 键时,此按钮会被“点击”。
<Button Content="普通按钮" Grid.Column="0"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="80" Height="30"/>
<Button Content="禁用按钮" IsEnabled="False" Grid.Column="1"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="80" Height="30"/>
<!-- 带有图标的按钮 -->
<Button Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="80" Height="30">
<StackPanel Orientation="Horizontal">
<Image Source="pack://application:,,,/YourAppName;component/Images/save.png" Width="16" Height="16" Margin="0,0,5,0"/>
<TextBlock Text="保存"/>
</StackPanel>
</Button>
5. 单选框 RadioButton
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Contentobject设置单选按钮旁边显示的文本或其他内容。 IsCheckedbool?获取或设置一个值,指示单选按钮是否被选中(true)或未选中(false)。 GroupNamestring将 RadioButton 归入一个逻辑组。在同一组内,只能有一个 RadioButton 被选中。
<TextBlock Text="请选择您的偏好设置:" Margin="0,15,0,0"/>
<!-- 未设置 GroupName 的 RadioButton 会独立于其他组 -->
<RadioButton Content="启用功能A" Margin="0,5,0,0"/>
<RadioButton Content="启用功能B" Margin="0,5,0,0"/>
6. CheckBox 复选框
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Contentobject设置复选框旁边显示的文本或其他内容。 IsCheckedbool?获取或设置一个值,指示复选框是否被选中(true)、未选中(false)或处于不确定状态(null)。 IsThreeStatebool设置复选框是否允许处于不确定(null)状态。如果为 true,点击时会依序在选中、未选中、不确定状态间切换。
<StackPanel Margin="10">
<CheckBox Content="我同意用户协议" IsChecked="True"/>
<CheckBox Content="接收营销邮件" IsChecked="False" Margin="0,5,0,0"/>
<CheckBox Content="开启高级功能 (不确定状态)" IsThreeState="True" IsChecked="{x:Null}" Margin="0,5,0,0"/>
</StackPanel>
四、常用控件(二)
1. 图片控件 Image
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) SourceImageSource指定要显示的图像文件的路径或 URI。支持多种图像格式(PNG, JPG, BMP, GIF等)。 StretchStretch定义图像如何调整大小以适应其可用空间:None (原大小), Fill (填充并可能变形), Uniform (保持比例填充), UniformToFill (保持比例填充并可能裁剪)。 StretchDirectionStretchDirection配合 Stretch 属性,控制图像是只放大、只缩小还是双向调整以适应内容区域。
<StackPanel Margin="10">
<!-- 假设你项目根目录下有 "my_image.png" 或在 Images 文件夹有 "icon.png" -->
<Image Source="my_image.png" Width="100" Height="100" Stretch="Uniform"/>
<Image Source="Images/icon.png" Stretch="Fill" Width="150" Height="80" Margin="0,10,0,0"/>
<Image Source="pack://application:,,,/YourAppName;component/Assets/logo.jpg"
Width="200" Height="50" Stretch="UniformToFill" Margin="0,10,0,0"/>
</StackPanel>
2. 滑块 Slider
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Valuedouble获取或设置滑块的当前选定值。用户拖动滑块时,此值会更新。 Minimumdouble设置滑块可选择的最小值。 Maximumdouble设置滑块可选择的最大值。Value 始终在 Minimum 和 Maximum 之间。 TickFrequencydouble当刻度线可见时,指定刻度线之间的值间隔。 TickPlacementTickPlacement控制刻度线相对于滑块轨道的显示位置(如 TopLeft, BottomRight, Both, None)。 IsSnapToTickEnabledbool决定滑块的“拇指”是否只能吸附到 TickFrequency 定义的刻度位置上。 OrientationOrientation设置滑块的方向:Horizontal (水平) 或 Vertical (垂直)。
<TextBlock Text="亮度:" Margin="0,10,0,0"/>
<Slider Orientation="Horizontal" Minimum="0" Maximum="10" Value="5"
TickPlacement="BottomRight" TickFrequency="1" IsSnapToTickEnabled="True"
Margin="0,5,0,0"/>
<TextBlock Text="垂直滑块:" Margin="0,10,0,0"/>
<Slider Orientation="Vertical" Minimum="0" Maximum="100" Value="25"
Width="50" Height="150" VerticalAlignment="Top" HorizontalAlignment="Left"
TickPlacement="TopLeft" TickFrequency="25"/>
3. 进度条 ProgressBar
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Valuedouble获取或设置进度条的当前进度值。 Minimumdouble设置进度条的最小值,通常为 0,表示操作的开始。 Maximumdouble设置进度条的最大值,通常为 100,表示操作的完成。 IsIndeterminatebool当设置为 true 时,进度条显示一个持续动画,表示操作正在进行但具体进度未知。此时 Value 属性被忽略。 OrientationOrientation设置进度条的方向:Horizontal (水平) 或 Vertical (垂直)。
<TextBlock Text="加载中 (不确定):" Margin="0,10,0,0"/>
<!-- 不确定模式,显示动画而非具体进度 -->
<ProgressBar IsIndeterminate="True" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"/>
<TextBlock Text="垂直进度条:" Margin="0,10,0,0"/>
<ProgressBar Orientation="Vertical" Value="30" Width="30" Height="100" HorizontalAlignment="Left" Margin="0,5,0,0"/>
4. 边框 Border
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) ChildUIElement设置 Border 内部包含的单个 UI 元素。如果需要包含多个,通常将一个布局面板(如 Grid 或 StackPanel)作为 Child。 BorderBrushBrush设置边框的颜色或样式(例如 SolidColorBrush, LinearGradientBrush)。 BorderThicknessThickness设置边框的粗细度。可以统一设置所有边(如 1),或分别设置(如 1,2,3,4 代表左、上、右、下)。 CornerRadiusCornerRadius设置边框的圆角半径,使边框呈现弧形。 BackgroundBrush设置 Border 内部区域(即 Child 元素所在的区域)的背景颜色或图片。 PaddingThickness设置 Border 内部内容(Child 元素)与其自身边框之间的间距。
<!-- 圆角边框 -->
<Border BorderBrush="Blue" BorderThickness="2" CornerRadius="10" Background="LightBlue" Padding="15"
Margin="0,15,0,0" HorizontalAlignment="Left">
<TextBlock Text="圆角边框示例" FontWeight="Bold" Foreground="White"/>
</Border>
<!-- 包含多个子元素的边框 (通过 StackPanel) -->
<Border BorderBrush="Green" BorderThickness="2,0,2,0" Background="LightGreen" Padding="5"
Margin="0,15,0,0" HorizontalAlignment="Left">
<StackPanel>
<TextBlock Text="边框内的" FontWeight="Bold"/>
<TextBlock Text="多个子元素"/>
<Button Content="一个按钮"/>
</StackPanel>
</Border>
5. 内容控件 ContentControl
常用属性 (Property Name) 属性类型 (Property Type) 作用 (Effect / Description) Contentobject获取或设置控件显示的核心内容。因为是 object 类型,它可以是任何数据(字符串、自定义对象)或任何单个 UI 元素。 ContentTemplateDataTemplate当 Content 是一个数据对象(而非 UI 元素)时,此模板定义如何将该数据对象渲染成 UI 元素。是数据绑定和 MVVM 的核心概念之一。 ContentTemplateSelectorDataTemplateSelector提供自定义逻辑,在运行时根据 Content 对象的属性或类型,动态选择合适的 DataTemplate 来显示内容。
五、 资源
StaticResource 在编译时即确定值,而 DynamicResource 则表示资源在运行时动态计算值
WPF 资源访问时按照就近原则(顺序为元素级>页面级>应用级)
WPF 资源词典查找顺序由内向外,先查找自己的key-value值,若未找到再去MergedDictionaries中继续查找
六、 绑定
1. 数据绑定概述
数据绑定的本质上是绑定目标和绑定源之间的桥梁。通常,每个绑定都有四个组件
绑定目标对象
目标属性
绑定源
要使用的绑定源中值的路径
如果控件要绑定的值就是源对象,可以使用 Path=. 或则 不指定 Path
绑定中的 Path 可以省略,因为 Binding 元素含有一个带参构造函数
七、 数据绑定 DataContext
一个 UI 元素只能有一个 DataContext
八、 数据绑定方向与 INotifyPropertyChange
1. 数据绑定方向
OneWay (单向:源 -> 目标) * 数据流: 从数据源 (ViewModel) 流向 UI 目标 (控件)。 * 用途: 主要用于显示数据,如 TextBlock、Label。 * 例子: ViewModel 的 UserName 属性变了,界面上的 TextBlock 会自动更新。但用户无法直接修改 TextBlock。
TwoWay (双向:源 <-> 目标) * 数据流: 数据可以在源和目标之间自由流动。 * 用途: 主要用于用户输入和编辑,如 TextBox、CheckBox、Slider。 * 例子:
ViewModel 的 UserName 属性变了,TextBox 的内容会更新。
用户在 TextBox 中输入了新名字,ViewModel 的 UserName 属性也会被自动更新。
OneWayToSource (单向到源:目标 -> 源) * 数据流: 从 UI 目标 (控件) 流向数据源 (ViewModel)。这是 TwoWay 的反向。 * 用途: 比较少见。比如,一个只写的属性,或者当一个控件的状态改变时,我们只想更新后台数据,但不想让后台数据反过来影响这个控件。
OneTime (一次性) * 数据流: 在绑定建立时(通常是程序启动时)从源流向目标一次,之后便断开连接。 * 用途: 用于显示不会再改变的静态数据,性能开销最小。
2. INotifyPropertyChanged 接口
INotifyPropertyChanged 是 System.ComponentModel 命名空间下的一个接口
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
a. 第一种用法
public class UserViewModel : INotifyPropertyChanged
{
// 1. 实现接口要求的事件
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
// 只有当值真正改变时才更新和通知
if (_name != value)
{
_name = value;
// 3. 当属性值改变后,调用辅助方法来触发事件
OnPropertyChanged();
}
}
}
private int _age;
public int Age
{
get { return _age; }
set
{
if (_age != value)
{
_age = value;
OnPropertyChanged();
}
}
}
// 2. 创建一个辅助方法来触发事件
// [CallerMemberName] 是一个神奇的特性,它会自动获取调用此方法的属性的名称
// 所以调用 OnPropertyChanged() 等同于 OnPropertyChanged("Name")
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
b. 第二种用法
public class Teacher : INotifyPropertyChanged
{
private String name;
public String Name
{
get {
return name;
}
set {
name = value;
// 内写参数
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
// 令入参可为空
protected virtual void OnPropertyChanged(String propertyName="")
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
九、 依赖属性
1. 什么是依赖属性
一个依赖属性由两部分组成:
一个静态的、只读的 DependencyProperty 字段。 这是属性的“注册证”。它在 WPF 属性系统中注册了这个属性,并定义了它的元数据(如默认值、属性改变时的回调等)。
一个传统的 C# 属性包装器 (CLR Wrapper)。 这是我们平时在代码中直接访问的 get 和 set。
关键点: 这个包装器不存储属性值 。它的 get 和 set 方法内部只是调用了 DependencyObject 基类的 GetValue() 和 SetValue() 方法,来从 WPF 属性系统中存取值。
using System.Windows;
using System.Windows.Controls;
public class MyCustomControl : Control
{
// 1. 注册依赖属性 (The "Registration")
// 这是一个公共的、静态的、只读的字段。
// 命名约定: 属性名 + "Property"
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register(
"MyText", // 属性名 (字符串形式)
typeof(string), // 属性的数据类型
typeof(MyCustomControl), // 拥有此属性的类的类型
new PropertyMetadata("default value") // 元数据: 包含默认值等信息
);
// 2. 提供 CLR 属性包装器 (The "Wrapper")
// 这是我们在代码中实际使用的属性
public string MyText
{
// get 和 set 内部只是调用基类的方法
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
}
十、 布局与附加属性
1. 网格布局 Grid
1.1. 常用属性
属性名 (Property Name) 说明 (Description) 定义网格结构 (在 <Grid> 标签上) RowDefinitions行定义集合 。用于在内部添加一个或多个 <RowDefinition> 来定义网格的行。ColumnDefinitions列定义集合 。用于在内部添加一个或多个 <ColumnDefinition> 来定义网格的列。Height (在 RowDefinition 上)定义行高 。可以是固定像素值 (如 100)、自动适应内容 (Auto) 或按比例占据剩余空间 (*, 2* 等)。Width (在 ColumnDefinition 上)定义列宽 。规则与 Height 相同,可以是像素、Auto 或 * 比例。ShowGridLines显示网格线 。一个布尔值 (True 或 False)。设置为 True 时会显示所有行和列的辅助线,非常适合在设计和调试时使用。放置子控件 (在 Grid 的子控件上,作为附加属性) Grid.Row指定控件所在的行索引 。索引从 0 开始。如果不指定,默认为 0。Grid.Column指定控件所在的列索引 。索引从 0 开始。如果不指定,默认为 0。Grid.RowSpan指定控件纵向跨越的行数 。例如,Grid.RowSpan="2" 表示控件将占据两行的高度。Grid.ColumnSpan指定控件横向跨越的列数 。例如,Grid.ColumnSpan="3" 表示控件将占据三列的宽度。子控件在单元格内的对齐 (在子控件上) HorizontalAlignment水平对齐方式 。决定控件在其所在单元格内的水平位置。可选值:Left, Right, Center, Stretch (默认, 水平撑满)。VerticalAlignment垂直对齐方式 。决定控件在其所在单元格内的垂直位置。可选值:Top, Bottom, Center, Stretch (默认, 垂直撑满)。Margin外边距 。设置控件与其所在单元格边界之间的距离,可以用来创建控件周围的空白区域。
1.2. 示例
<!-- 步骤 1: 定义列的结构 -->
<Grid.ColumnDefinitions>
<!-- 第0列: 用于放置标签。Width="Auto" 使其宽度自动适应最长的标签文字。 -->
<ColumnDefinition Width="Auto"/>
<!-- 第1列: 用于放置输入框。Width="*" 使其占据所有剩余的水平空间。-->
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 步骤 2: 定义行的结构 -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- 第0行: 标题行,高度自适应内容。 -->
<RowDefinition Height="Auto"/> <!-- 第1行: 姓氏输入行,高度自适应。 -->
<RowDefinition Height="Auto"/> <!-- 第2行: 名字输入行,高度自适应。 -->
<RowDefinition Height="Auto"/> <!-- 第3行: 邮箱输入行,高度自适应。 -->
<RowDefinition Height="*"/> <!-- 第4行: 备注区,高度占据所有剩余的垂直空间,实现响应式。 -->
<RowDefinition Height="50"/> <!-- 第5行: 按钮行,固定高度50。 -->
</Grid.RowDefinitions>
<!-- 步骤 3: 放置控件到指定的单元格 -->
<!-- 标题: 位于第0行,从第0列开始,并横跨2列 (ColumnSpan="2") -->
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
Text="创建新用户" FontSize="20" FontWeight="Bold"
HorizontalAlignment="Center" Margin="0,0,0,20"/>
<!-- 姓氏 (Last Name) -->
<Label Grid.Row="1" Grid.Column="0" Content="姓氏(L):" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="5" VerticalAlignment="Center"/>
<!-- 名字 (First Name) -->
<Label Grid.Row="2" Grid.Column="0" Content="名字(F):" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Margin="5" VerticalAlignment="Center"/>
<!-- 邮箱 (Email) -->
<Label Grid.Row="3" Grid.Column="0" Content="电子邮件:" VerticalAlignment="Center"/>
<TextBox Grid.Row="3" Grid.Column="1" Margin="5" VerticalAlignment="Center"/>
<!-- 备注 (Notes) -->
<Label Grid.Row="4" Grid.Column="0" Content="备注:" VerticalAlignment="Top" Margin="0,5,0,0"/>
<TextBox Grid.Row="4" Grid.Column="1" Margin="5"
TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto"/>
<!-- 按钮组: 放置在一个 StackPanel 中,然后将 StackPanel 放置在单元格中 -->
<StackPanel Grid.Row="5" Grid.Column="1" Orientation="Horizontal"
HorizontalAlignment="Right" VerticalAlignment="Center">
<Button Content="提交" Width="80" Margin="5"/>
<Button Content="取消" Width="80" Margin="5"/>
</StackPanel>
2. 统一网格 UniformGrid
2.1. 常用属性
属性名 说明 Rows指定网格的行数 。这是一个整数。<br>如果设置了 Rows,Columns 会被自动计算。Columns指定网格的列数 。这是一个整数。<br>如果设置了 Columns,Rows 会被自动计算。FirstColumn指定第一个子控件应该放置在哪一列 (索引从 0 开始)。<br>默认是 0。这个属性可以用来创建“偏移”的布局,但使用较少。
2.2. 示例
<UniformGrid Rows="3" Columns="3" FirstColumn="1">
<Button Content="Button1" />
<Button Content="Button2" />
<Button Content="Button3" />
<Button Content="Button4" />
<Button Content="Button5" />
<Button Content="Button6" />
<Button Content="Button7" />
<Button Content="Button8" />
</UniformGrid>
3. 堆叠面板 StackPanel
3.1. 常用属性
属性名 说明 Orientation获取或设置一个值,该值指示子元素的堆叠维度。 Horizontal 水平,Vertical 垂直
3.2. 示例
<StackPanel Orientation="Horizontal">
<Button Content="Button"/>
<Button Content="Button"/>
<Button Content="Button"/>
<Button Content="Button"/>
</StackPanel>
4. 包裹面板 WrapPanel
4.1. 常用属性
属性名 说明 Orientation决定了子元素的排列和换行方向 。这是最重要的属性。<br>• Horizontal (默认值): 元素从左到右排列。当碰到容器的右边界时,换到下一行,并从左边重新开始。<br>• Vertical: 元素从上到下排列。当碰到容器的下边界时,换到下一列,并从顶部重新开始。ItemWidth为 WrapPanel 中的所有子元素 设置一个统一的宽度。如果你设置了这个值,它会覆盖子元素自身的 Width 属性。 ItemHeight为 WrapPanel 中的所有子元素 设置一个统一的高度。如果你设置了这个值,它会覆盖子元素自身的 Height 属性。
4.2. 示例
<WrapPanel Orientation="Horizontal" ItemHeight="30" ItemWidth="30">
<Button Content="Button"/>
<Button Content="Button"/>
<Button Content="Button"/>
<Button Content="Button"/>
</WrapPanel>
5. 停靠面板 DockPanel
5.1. 常用属性
属性名 类型 说明 DockPanel.Dock附加属性 这是最重要的属性,写在 DockPanel 的子控件 上。它告诉 DockPanel 应该把这个子控件停靠在哪个方向。<br>可选值:Left, Right, Top, Bottom。 LastChildFill自身属性 (布尔值) 这个属性写在 <DockPanel> 标签上。决定了最后一个子元素是否应该自动填充剩余空间。<br>• True (默认值): 最后一个子元素自动填充。<br>• False: 最后一个子元素不会填充,它会根据自身的对齐属性来定位,剩余空间会留白。
5.2. 示例
<DockPanel LastChildFill="True">
<Button Content="Button" DockPanel.Dock="Top"/>
<Button Content="Button" DockPanel.Dock="Left"/>
<Button Content="Button" DockPanel.Dock="Right"/>
<Button Content="Button" DockPanel.Dock="Bottom"/>
</DockPanel>
6. 画布面板 Canvas
6.1. 常用属性
属性名 说明 Canvas.Left指定子元素左上角 相对于 Canvas 左边缘 的距离 (X 坐标)。 Canvas.Top指定子元素左上角 相对于 Canvas 上边缘 的距离 (Y 坐标)。 Canvas.Right指定子元素右边缘 相对于 Canvas 右边缘 的距离。 Canvas.Bottom指定子元素下边缘 相对于 Canvas 下边缘 的距离。
6.2. 示例
<Canvas>
<Button Content="1" Canvas.Left="100"/>
<Button Content="2" Canvas.Left="200"/>
<Button Content="3" Canvas.Top="100"/>
<Button Content="4" Canvas.Bottom="200"/>
</Canvas>
十一、 数据模板DataTemplate与数据模板选择器DataTemplateSelector
<ContentControl Margin="220,70,345,303">
<local:Teacher Name="张三老师" Desc="擅长高等数学"></local:Teacher>
</ContentControl>
<ContentControl Margin="220,151,345,217">
<local:Student Name="学生" Age="20"/>
</ContentControl>
<ContentControl Margin="220,249,345,123">
<local:Student Name="学生" Age="20" />
</ContentControl>
如果不提供 DataTemplate, 对于复杂数据,控件显示数据会默认调用对象的 Tostring() 方法转为字符串显示
1. 数据模板的三种用法
1.1. 在资源中描述 Teacher 类型数据显示的数据模板资源
<Window.Resources>
<!--在资源中描述 Teacher类型数据显示的数据模板资源-->
<DataTemplate DataType="{x:Type local:Teacher}">
<Border BorderBrush="red" BorderThickness="3">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Desc}" />
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
1.2. 在控件中定义数据模板
<ContentControl Margin="220,151,345,217">
<ContentControl.ContentTemplate>
<DataTemplate>
<Border BorderBrush="Green" Background="Yellow" BorderThickness="3">
<StackPanel>
<Button Content="{Binding Name}" />
<Button Content="{Binding Age}" />
</StackPanel>
</Border>
</DataTemplate>
</ContentControl.ContentTemplate>
<local:Student Name="学生" Age="20" />
</ContentControl>
1.3. 在资源中的数据模板定义 Key
<Window.Resources>
<!--在资源中的数据模板定义Key-->
<DataTemplate x:Key="myDataTemplate">
<Border BorderBrush="Gray" BorderThickness="3">
<StackPanel >
<TextBlock Text="{Binding Name}" />
<ProgressBar Height="10" Value="{Binding Age}" />
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl ContentTemplate="{StaticResource myDataTemplate}" Margin="220,249,345,123">
<local:Student Name="王五" Age="28" />
</ContentControl>
</Grid>
2. 数据模板选择器
c# 代码
using System.Windows;
using System.Windows.Controls;
public class Student
{
public string Name { get; set; }
public double Score { get; set; }
}
public class StudentTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// 1. 获取当前数据项
Student student = item as Student;
if (student == null)
{
return base.SelectTemplate(item, container);
}
// 2. 获取承载模板的容器 (例如 ListBox)
FrameworkElement element = container as FrameworkElement;
if (element != null && student != null)
{
// 3. 根据 Student 对象的 Score 属性来选择模板
if (student.Score >= 90)
{
// 从容器的资源中查找名为 "ExcellentStudentTemplate" 的模板
return element.FindResource("ExcellentStudentTemplate") as DataTemplate;
}
else if (student.Score < 60)
{
return element.FindResource("WarningStudentTemplate") as DataTemplate;
}
}
// 默认情况下,返回 null,WPF 会寻找默认模板
return null;
// 或者也可以返回一个默认模板: return element.FindResource("NormalStudentTemplate") as DataTemplate;
}
}
xaml 代码
<Window.Resources>
<!-- 优秀学生的模板 (金色背景) -->
<DataTemplate x:Key="ExcellentStudentTemplate">
<Border Background="Gold" BorderBrush="Goldenrod" BorderThickness="1" CornerRadius="4" Padding="8" Margin="2">
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Score, StringFormat='分数: {0}'}" />
</StackPanel>
</Border>
</DataTemplate>
<!-- 警告学生的模板 (黄色背景) -->
<DataTemplate x:Key="WarningStudentTemplate">
<Border Background="LightYellow" BorderBrush="Orange" BorderThickness="1" CornerRadius="4" Padding="8" Margin="2">
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Score, StringFormat='分数: {0}'}" Foreground="Red" />
</StackPanel>
</Border>
</DataTemplate>
<!-- 为普通学生准备一个隐式模板,作为默认选项 -->
<DataTemplate DataType="{x:Type local:Student}">
<Border Background="WhiteSmoke" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Padding="8" Margin="2">
<StackPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Score, StringFormat='分数: {0}'}" />
</StackPanel>
</Border>
</DataTemplate>
<!-- 实例化我们的选择器类 -->
<local:StudentTemplateSelector x:Key="MyStudentSelector" />
</Window.Resources>
<!-- 然后,在 ListBox 中使用它 -->
<ListBox ItemsSource="{Binding StudentList}" HorizontalContentAlignment="Stretch">
<!--
使用 ItemTemplateSelector 属性,而不是 ItemTemplate。
它会为列表中的每一项调用 MyStudentSelector 的 SelectTemplate 方法。
-->
<ListBox.ItemTemplateSelector>
<StaticResource ResourceKey="MyStudentSelector"/>
</ListBox.ItemTemplateSelector>
</ListBox>
十二、 下拉框控件 ComboBox 与数据模板
1. 常用属性
1.1. 数据填充属性
属性 描述 ItemsSource最重要的属性 。用于绑定一个数据集合(如 List<T>, ObservableCollection<T>)。这是实现数据驱动 UI 的核心。Items一个 ItemCollection 集合。当你不使用 ItemsSource 时,可以通过它直接添加或移除项(如 <ComboBoxItem>)。不推荐在业务代码中频繁操作,主要用于静态的 XAML 定义。 HasItems(只读) 一个布尔值,表示 ComboBox 中是否有任何项。
1.2. 显示与模版属性
属性 描述 DisplayMemberPath最快捷的显示方式 。指定 ItemsSource 集合中对象的某个属性路径(如 "Name"),用该属性的文本值 作为列表项的显示内容。ItemTemplate最灵活的显示方式 。接收一个 DataTemplate,用于自定义下拉列表中每一项的复杂外观(比如同时显示图片、姓名、分数等)。 DisplayMemberPath 和 ItemTemplate 通常只用其一。ItemStringFormat一个格式化字符串,用于格式化由 DisplayMemberPath 指定的显示内容。例如,对于日期可以设置为 "{}{0:yyyy-MM-dd}"。 ItemTemplateSelector数据模板选择器。当你想根据数据项的不同(如学生分数高低),为同一列表中的项应用不同的 DataTemplate 时使用。
1.3. 选择相关属性
属性 描述 SelectedItemMVVM 模式下的首选 。获取或设置被选中的整个数据对象 。例如,如果 ItemsSource 绑定了 List<Student>,那么 SelectedItem 就是那个被选中的 Student 对象。SelectedIndex获取或设置被选中项的索引 (int 类型,从 0 开始)。值为 -1 表示没有选中项。适合简单场景或需要按位置操作的场景。 SelectedValue获取或设置由 SelectedValuePath 指定的特定属性的值 。 SelectedValuePath与 SelectedValue 配套使用。指定当一项被选中时,SelectedValue 属性应该获取该项对象的哪个属性的值。例如,你可以在下拉框显示学生姓名 (DisplayMemberPath="Name"),但你实际需要获取的是他的学号 (SelectedValuePath="Id")。
1.4. 行为与外观属性
属性 描述 IsEditable布尔值。如果为 True,ComboBox 的头部会变成一个可编辑的 TextBox,允许用户输入文本。默认为 False。 Text当 IsEditable="True" 时,此属性获取或设置编辑框中的文本。你也可以通过绑定它来实现搜索和自动完成功能。 IsDropDownOpen布尔值。获取或设置下拉列表是否展开。你可以通过绑定或在代码中设置它来手动打开或关闭下拉框。 StaysOpenOnEdit布尔值。当 IsEditable="True" 时,如果此属性为 True,则在用户编辑文本时下拉列表保持打开状态。 MaxDropDownHeight设置下拉列表展开时的最大高度。超出部分会出现滚动条。 IsEnabled, Width, Height, Margin, FontSize, Foreground 等标准的 FrameworkElement 属性,用于控制控件的基本尺寸、位置、可用状态和字体样式。
1.5. 常用事件
事件 描述 SelectionChanged最重要的事件 。当 SelectedItem 发生改变时触发。这是在后台代码 (code-behind) 中响应用户选择的主要方式。DropDownOpened当下拉列表被打开时触发。 DropDownClosed当下拉列表被关闭时触发。
2. 填写内容的三种实现
<Grid>
<!--ComboBox.ItemsSource实现-->
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="285,89,0,0" VerticalAlignment="Top" Width="216" Height="27" FontSize="20">
<ComboBox.ItemsSource>
<Collections:ArrayList>
<system:String>高等数学</system:String>
<system:String>线性代数</system:String>
<system:String>数率统计</system:String>
<Rectangle Fill="red" Width="30" Height="20"/>
</Collections:ArrayList>
</ComboBox.ItemsSource>
</ComboBox>
<!--ComboBoxItem实现-->
<ComboBox x:Name="comboBox2" HorizontalAlignment="Left" Margin="285,160,0,0" VerticalAlignment="Top" Width="216" Height="27" FontSize="20">
<ComboBoxItem>美术</ComboBoxItem>
<ComboBoxItem>音乐</ComboBoxItem>
<ComboBoxItem>体育</ComboBoxItem>
</ComboBox>
<!--C#代码实现-->
<ComboBox x:Name="comboBox3" HorizontalAlignment="Left" Margin="285,229,0,0" VerticalAlignment="Top" Width="216" Height="27" FontSize="20"/>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<string> list = new List<string>();
list.Add("C++");
list.Add("Java");
list.Add("Python");
//数据源赋值
comboBox3.ItemsSource = list;
}
}
Comments NOTHING