1.1 什么是 WPF?
1.1.1 WPF 的定位
WPF = Windows Presentation Foundation,是微软在 2006 年推出的现代 GUI 框架,用于替代传统的 WinForms。
text
┌─────────────────────────────────────────────────────────────────┐
│ Windows 桌面应用技术演进 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Win32/MFC ──────> WinForms ──────> WPF ──────> WinUI/MAUI │
│ (1990s) (2002) (2006) (2020+) │
│ │
│ 底层 API 简单封装 GPU 加速 跨平台 │
│ 难以使用 不够灵活 矢量渲染 现代设计 │
│ │
└─────────────────────────────────────────────────────────────────┘
1.1.2 WPF 的核心特点
| 特点 | 说明 |
|---|---|
| XAML 分离 | UI 和逻辑分离,设计师和开发者可并行工作 |
| 矢量渲染 | 基于 GPU 渲染,任意缩放不失真 |
| 数据绑定 | 强大的绑定机制,UI 和数据自动同步 |
| 样式和模板 | 完全自定义控件外观,无需修改代码 |
| 3D 支持 | 可在应用中嵌入 3D 内容 |
| 动画系统 | 基于时间线的流畅动画 |
1.1.3 WPF vs WinForms
csharp
// WinForms:基于像素,控件直接操作
button1.Text = "点击我";
button1.Click += Button1_Click;
// WPF:基于 XAML,声明式 UI
// MainWindow.xaml
<Button x:Name="MyButton" Content="点击我" Click="MyButton_Click" />
// MainWindow.xaml.cs
private void MyButton_Click(object sender, RoutedEventArgs e)
{
MyButton.Content = "已点击";
}
1.2 开发环境搭建
1.2.1 安装 Visual Studio 2022
-
下载 Visual Studio 2022 Community(免费)
-
安装时勾选 .NET 桌面开发 工作负载
text
勾选内容:
☑ .NET 桌面开发
├── .NET Framework 4.8 开发工具
├── 适用于 .NET 的 WPF 和 WinForms 设计器
└── C# 和 Visual Basic 语言支持
1.2.2 创建第一个 WPF 项目
bash
# 使用 CLI 创建
dotnet new wpf -n MyFirstWpfApp
cd MyFirstWpfApp
dotnet run
或通过 Visual Studio:
-
文件 → 新建 → 项目
-
搜索 "WPF" → 选择 WPF 应用程序
-
设置项目名称和位置 → 创建
1.2.3 项目结构
text
MyFirstWpfApp/
├── App.xaml # 应用级资源、启动设置
├── App.xaml.cs # App 后台代码
├── MainWindow.xaml # 主窗口 UI(XAML)
├── MainWindow.xaml.cs # 主窗口逻辑(C#)
├── AssemblyInfo.cs # 程序集信息
└── bin/ # 编译输出
1.3 XAML 基础
1.3.1 什么是 XAML?
XAML = eXtensible Application Markup Language,由微软开发的声明式 UI 语言,用于描述 WPF 界面。
xml
<!-- 示例:XAML 描述了"一个按钮在窗口中的样子" -->
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="我的应用" Height="450" Width="800">
<Grid>
<Button Content="点击我"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="100" Height="40"/>
</Grid>
</Window>
1.3.2 XAML 命名空间
xml
<!-- 核心 WPF 命名空间 -->
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<!-- XAML 语言命名空间 -->
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
<!-- 系统命名空间(引用 System 命名空间中的类型)-->
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<!-- 自定义控件命名空间 -->
xmlns:local="clr-namespace:MyApp"
xmlns:controls="clr-namespace:MyApp.Controls"
1.3.3 XAML 中的类型和属性
xml
<!-- 对象元素语法:使用尖括号创建对象 -->
<Button>
<Button.Content>按钮文字</Button.Content>
</Button>
<!-- 属性语法:使用 = 和 "" -->
<Button Content="按钮文字" Width="100" />
<!-- 属性元素语法:复杂属性值 -->
<Button>
<Button.Background>
<LinearGradientBrush>
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Blue" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
<!-- 附加属性:由其他控件提供的属性 -->
<Grid>
<Button Grid.Row="0" Grid.Column="1" />
</Grid>
1.3.4 标记扩展
xml
<!-- 绑定标记扩展 -->
<TextBlock Text="{Binding UserName}" />
<!-- 静态资源引用 -->
<Button Background="{StaticResource PrimaryBrush}" />
<!-- 动态资源引用 -->
<Button Style="{DynamicResource ButtonStyle}" />
<!-- 空标记扩展 -->
<TextBlock Text="{x:Null}" />
<!-- 静态成员引用 -->
<TextBlock Text="{x:Static sys:Environment.MachineName}" />
<!-- 类型引用 -->
<ObjectDataProvider x:Key="data" ObjectType="{x:Type local:MyClass}" />
1.4 WPF 布局系统
1.4.1 布局容器对比
| 容器 | 布局方式 | 适用场景 |
|---|---|---|
Grid |
表格布局(行/列) | 复杂布局,最常用 |
StackPanel |
垂直/水平堆叠 | 简单列表、工具栏 |
WrapPanel |
自动换行排列 | 标签云、图片墙 |
DockPanel |
边缘停靠 | IDE 风格布局 |
Canvas |
绝对定位 | 绘图、拖拽操作 |
Border |
装饰边框 | 包装单个元素 |
1.4.2 Grid 详解
xml
<Window x:Class="LayoutDemo.MainWindow"
Title="Grid 布局示例" Height="400" Width="600">
<Grid>
<!-- 定义行和列 -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- 自适应内容 -->
<RowDefinition Height="*"/> <!-- 按比例占用剩余空间 -->
<RowDefinition Height="2*"/> <!-- 2 倍比例 -->
<RowDefinition Height="100"/> <!-- 固定高度 -->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<!-- 标题栏(跨所有列) -->
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
Background="SkyBlue">
<TextBlock Text="应用标题" FontSize="20" Margin="10"/>
</Border>
<!-- 左侧导航栏 -->
<Border Grid.Row="1" Grid.RowSpan="2" Grid.Column="0"
Background="LightGray" Width="200">
<StackPanel Margin="10">
<Button Content="首页" Margin="0,5"/>
<Button Content="设置" Margin="0,5"/>
<Button Content="关于" Margin="0,5"/>
</StackPanel>
</Border>
<!-- 主内容区 -->
<Border Grid.Row="1" Grid.Column="1" Background="White">
<TextBlock Text="主内容区域" Margin="10"/>
</Border>
<!-- 右侧边栏 -->
<Border Grid.Row="1" Grid.Column="2" Background="LightYellow">
<TextBlock Text="侧边栏" Margin="10"/>
</Border>
<!-- 状态栏 -->
<Border Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"
Background="LightGreen" Height="30">
<TextBlock Text="就绪" Margin="10,5"/>
</Border>
</Grid>
</Window>
1.4.3 StackPanel 详解
xml
<!-- 垂直 StackPanel -->
<StackPanel Orientation="Vertical" Margin="10">
<Label Content="个人信息" FontWeight="Bold"/>
<TextBox x:Name="NameBox" Margin="0,5" Height="30"/>
<TextBox x:Name="EmailBox" Margin="0,5" Height="30"/>
<Button Content="提交" Width="100" HorizontalAlignment="Right"/>
</StackPanel>
<!-- 水平 StackPanel(工具栏)-->
<ToolBar>
<StackPanel Orientation="Horizontal">
<Button Content="新建"/>
<Button Content="打开"/>
<Button Content="保存"/>
<Separator Width="10"/>
<Button Content="剪切"/>
<Button Content="复制"/>
<Button Content="粘贴"/>
</StackPanel>
</ToolBar>
1.4.4 WrapPanel 详解
xml
<!-- 标签云效果 -->
<WrapPanel Margin="10" ItemHeight="30">
<Button Content="C#" Background="LightBlue" Margin="2"/>
<Button Content="WPF" Background="LightGreen" Margin="2"/>
<Button Content="XAML" Background="LightYellow" Margin="2"/>
<Button Content="Data Binding" Background="LightPink" Margin="2"/>
<Button Content="MVVM" Background="LightCoral" Margin="2"/>
<Button Content="Async" Background="LightCyan" Margin="2"/>
<!-- 窗口缩小时自动换行 -->
</WrapPanel>
1.4.5 DockPanel 详解
xml
<!-- VS 风格布局 -->
<DockPanel>
<!-- 顶部菜单栏 -->
<Menu DockPanel.Dock="Top">
<MenuItem Header="文件"/>
<MenuItem Header="编辑"/>
<MenuItem Header="视图"/>
</Menu>
<!-- 左侧工具窗口 -->
<StackPanel DockPanel.Dock="Left" Width="200">
<Button Content="工具1"/>
<Button Content="工具2"/>
</StackPanel>
<!-- 右侧属性窗口 -->
<StackPanel DockPanel.Dock="Right" Width="250">
<GroupBox Header="属性"/>
</StackPanel>
<!-- 底部状态栏 -->
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem>就绪</StatusBarItem>
</StatusBar>
<!-- 中央内容区(自动填充剩余空间)-->
<RichTextBox/>
</DockPanel>
1.4.6 Canvas 详解(绝对定位)
xml
<!-- 绘图和拖拽场景 -->
<Canvas Background="LightGray">
<!-- 绝对定位 -->
<Button Canvas.Left="50" Canvas.Top="100" Content="按钮"/>
<Rectangle Canvas.Left="200" Canvas.Top="150" Width="100" Height="50"
Fill="Red" RadiusX="5" RadiusY="5"/>
<!-- 支持拖拽 -->
<Ellipse x:Name="DraggableCircle"
Canvas.Left="300" Canvas.Top="100"
Width="50" Height="50" Fill="Blue"
MouseLeftButtonDown="Circle_MouseLeftButtonDown"
MouseMove="Circle_MouseMove"/>
</Canvas>
1.4.7 Border 详解(装饰容器)
xml
<!-- Border 只能包含一个子元素 -->
<Border Background="White"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="8"
Padding="10"
Margin="5">
<StackPanel>
<TextBlock Text="带边框的面板" FontWeight="Bold"/>
<TextBlock Text="内容..." Margin="0,5"/>
</StackPanel>
</Border>
<!-- 制作阴影效果 -->
<Border Background="LightBlue" Margin="20" Padding="20">
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="5" Opacity="0.5"/>
</Border.Effect>
<TextBlock Text="带阴影的控件"/>
</Border>
1.5 常用控件
1.5.1 基础控件
| 控件 | 用途 | 核心属性 |
|---|---|---|
Button |
按钮 | Content, Click |
Label |
文本标签 | Content |
TextBox |
文本输入 | Text, MaxLength |
PasswordBox |
密码输入 | Password |
CheckBox |
复选框 | IsChecked |
RadioButton |
单选按钮 | IsChecked, GroupName |
ComboBox |
下拉选择 | ItemsSource, SelectedItem |
ListBox |
列表选择 | ItemsSource, SelectedItem |
Slider |
滑块 | Value, Minimum, Maximum |
ProgressBar |
进度条 | Value, IsIndeterminate |
DatePicker |
日期选择 | SelectedDate |
1.5.2 控件使用示例
xml
<StackPanel Margin="10" Width="300">
<!-- 文本框和标签 -->
<Label Content="用户名" FontWeight="Bold"/>
<TextBox x:Name="UserNameBox" Margin="0,5,0,15"/>
<Label Content="密码" FontWeight="Bold"/>
<PasswordBox x:Name="PasswordBox" Margin="0,5,0,15"/>
<!-- 复选框 -->
<CheckBox x:Name="RememberCheckBox" Content="记住密码" Margin="0,5"/>
<!-- 单选按钮 -->
<StackPanel Orientation="Horizontal" Margin="0,10">
<Label Content="性别:" FontWeight="Bold"/>
<RadioButton GroupName="Gender" Content="男" IsChecked="True"/>
<RadioButton GroupName="Gender" Content="女" Margin="20,0,0,0"/>
</StackPanel>
<!-- 下拉选择 -->
<Label Content="职业" FontWeight="Bold"/>
<ComboBox x:Name="JobCombo" Margin="0,5,0,15"/>
<!-- 进度条和滑块 -->
<Label Content="音量"/>
<Slider x:Name="VolumeSlider" Minimum="0" Maximum="100" Value="50"/>
<ProgressBar Value="{Binding Value, ElementName=VolumeSlider}" Height="20"/>
<!-- 日期选择 -->
<Label Content="出生日期" FontWeight="Bold"/>
<DatePicker x:Name="BirthDatePicker" Margin="0,5,0,15"/>
<!-- 按钮 -->
<Button x:Name="LoginButton" Content="登录"
Background="SkyBlue" Height="35" Margin="0,10"
Click="LoginButton_Click"/>
</StackPanel>
1.5.3 数据控件
xml
<!-- ListBox 示例 -->
<ListBox x:Name="StudentList" Height="200">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="2" Padding="5" CornerRadius="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Width="100"/>
<TextBlock Text="{Binding Age}" Width="50"/>
<TextBlock Text="{Binding Grade}" Width="100"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- ListView(带列标题)-->
<ListView x:Name="ProductListView">
<ListView.View>
<GridView>
<GridViewColumn Header="名称" Width="150" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="价格" Width="80" DisplayMemberBinding="{Binding Price, StringFormat=C}"/>
<GridViewColumn Header="库存" Width="80" DisplayMemberBinding="{Binding Stock}"/>
</GridView>
</ListView.View>
</ListView>
<!-- DataGrid(表格控件)-->
<DataGrid x:Name="UserGrid" AutoGenerateColumns="False"
CanUserAddRows="True" CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="50"/>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="120"/>
<DataGridTextColumn Header="邮箱" Binding="{Binding Email}" Width="200"/>
<DataGridCheckBoxColumn Header="启用" Binding="{Binding IsActive}" Width="60"/>
<DataGridTemplateColumn Header="操作" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="编辑" Click="EditButton_Click"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
1.6 事件处理
1.6.1 常见事件
csharp
// MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// 代码中添加事件(也可以在 XAML 中添加)
Loaded += MainWindow_Loaded;
Closing += MainWindow_Closing;
}
// 窗口加载事件
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Title = "WPF 应用已启动";
InitializeData();
}
// 窗口关闭事件
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
var result = MessageBox.Show("确定要退出吗?", "确认",
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.No)
{
e.Cancel = true; // 取消关闭
}
}
// 按钮点击事件
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("数据已保存", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
// 文本框文本变化事件
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
{
string keyword = SearchBox.Text;
// 执行搜索
PerformSearch(keyword);
}
// 下拉选择变化事件
private void CategoryCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CategoryCombo.SelectedItem != null)
{
LoadProductsByCategory(CategoryCombo.SelectedItem.ToString());
}
}
// 鼠标事件
private void Button_MouseEnter(object sender, MouseEventArgs e)
{
((Button)sender).Background = Brushes.LightGray;
}
private void Button_MouseLeave(object sender, MouseEventArgs e)
{
((Button)sender).Background = Brushes.LightBlue;
}
}
1.6.2 路由事件
WPF 的事件有三种路由策略:
csharp
// 1. 冒泡事件(Bubbling):从触发元素向上传播
// <Window> ← <Grid> ← <StackPanel> ← <Button>(起点)
// 2. 隧道路由事件(Tunneling):从根元素向下传播(Preview 开头)
// <Button> ← <StackPanel> ← <Grid> ← <Window>(起点)
// 3. 直接事件(Direct):只在源元素上触发
// 在父控件中处理子控件的事件
// XAML
<StackPanel Button.Click="CommonButton_Click">
<Button Content="按钮1"/>
<Button Content="按钮2"/>
<Button Content="按钮3"/>
</StackPanel>
// 后台代码
private void CommonButton_Click(object sender, RoutedEventArgs e)
{
Button clickedButton = e.OriginalSource as Button;
MessageBox.Show($"点击了:{clickedButton.Content}");
}
// 使用隧道路由事件(PreviewMouseDown)
private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
// 在窗口所有鼠标点击前执行
Debug.WriteLine($"鼠标点击位置:{e.GetPosition(this)}");
}
1.6.3 附加事件
xml
<!-- 使用附加事件 -->
<StackPanel ButtonBase.Click="StackPanel_Click">
<Button Name="Btn1" Content="按钮1"/>
<Button Name="Btn2" Content="按钮2"/>
<Button Name="Btn3" Content="按钮3"/>
</StackPanel>
1.7 样式(Style)
1.7.1 内联样式
xml
<Button Content="按钮"
FontSize="14"
FontWeight="Bold"
Background="LightBlue"
Foreground="DarkBlue"
BorderBrush="DarkBlue"
BorderThickness="2"
Width="100"
Height="30"
Margin="5"/>
1.7.2 资源样式(推荐)
xml
<Window.Resources>
<!-- 全局按钮样式 -->
<Style x:Key="PrimaryButton" TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="DarkBlue"/>
<Setter Property="BorderBrush" Value="DarkBlue"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Margin" Value="5"/>
<!-- 鼠标悬停效果 -->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="SkyBlue"/>
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="DodgerBlue"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 文本框样式 -->
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="12"/>
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Margin" Value="0,5"/>
<Setter Property="Height" Value="30"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="Blue"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<!-- 使用样式 -->
<Button Style="{StaticResource PrimaryButton}" Content="确定"/>
<TextBox Style="{StaticResource TextBoxStyle}"/>
1.7.3 隐式样式(作用于所有目标类型)
xml
<Window.Resources>
<!-- 所有 Button 自动应用此样式(没有 x:Key)-->
<Style TargetType="Button">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Margin" Value="5"/>
</Style>
</Window.Resources>
1.7.4 样式继承
xml
<Window.Resources>
<!-- 基础样式 -->
<Style x:Key="BaseStyle" TargetType="Control">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Margin" Value="5"/>
</Style>
<!-- 基于基础样式的按钮样式 -->
<Style x:Key="ButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseStyle}">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Padding" Value="15,8"/>
</Style>
<!-- 基于基础样式的文本框样式 -->
<Style x:Key="TextBoxStyle" TargetType="TextBox" BasedOn="{StaticResource BaseStyle}">
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
</Window.Resources>
1.8 综合示例:登录窗口
xml
<!-- LoginWindow.xaml -->
<Window x:Class="WpfDemo.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="用户登录" Height="400" Width="500"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize">
<Window.Resources>
<!-- 标题样式 -->
<Style x:Key="TitleStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="DarkBlue"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,30,0,20"/>
</Style>
<!-- 标签样式 -->
<Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- 文本框样式 -->
<Style x:Key="InputStyle" TargetType="TextBox">
<Setter Property="Height" Value="35"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderBrush" Value="#CCCCCC"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,5"/>
<Setter Property="Margin" Value="0,5,0,15"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="#2196F3"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 按钮样式 -->
<Style x:Key="LoginButtonStyle" TargetType="Button">
<Setter Property="Height" Value="40"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background" Value="#2196F3"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Margin" Value="0,20,0,0"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1976D2"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#0D47A1"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 错误消息样式 -->
<Style x:Key="ErrorStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Margin" Value="0,-10,0,5"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 头部区域 -->
<Border Grid.Row="0" Background="#2196F3">
<StackPanel>
<TextBlock Text="欢迎使用 WPF 应用" Style="{StaticResource TitleStyle}"
Foreground="White"/>
<TextBlock Text="请登录您的账户"
HorizontalAlignment="Center"
Foreground="White" Opacity="0.9"/>
</StackPanel>
</Border>
<!-- 表单区域 -->
<Border Grid.Row="1" Padding="40,20">
<StackPanel>
<!-- 用户名 -->
<Label Content="用户名" Style="{StaticResource LabelStyle}"/>
<TextBox x:Name="UserNameBox" Style="{StaticResource InputStyle}"/>
<!-- 密码 -->
<Label Content="密码" Style="{StaticResource LabelStyle}"/>
<PasswordBox x:Name="PasswordBox"
Style="{StaticResource InputStyle}"
Height="35"/>
<!-- 验证码 -->
<Label Content="验证码" Style="{StaticResource LabelStyle}"/>
<WrapPanel>
<TextBox x:Name="CaptchaBox"
Width="150"
Style="{StaticResource InputStyle}"
Margin="0,5,10,0"/>
<Border Width="120" Height="40" Background="LightGray" Margin="0,5,0,0">
<TextBlock x:Name="CaptchaText"
Text="A3F9"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Bold"
FontSize="18"/>
</Border>
</WrapPanel>
<!-- 错误消息 -->
<TextBlock x:Name="ErrorMsg" Style="{StaticResource ErrorStyle}" Visibility="Collapsed"/>
<!-- 记住密码复选框 -->
<CheckBox x:Name="RememberCheckBox" Content="记住密码" Margin="0,15,0,0"/>
<!-- 登录按钮 -->
<Button x:Name="LoginButton"
Content="登录"
Style="{StaticResource LoginButtonStyle}"
Click="LoginButton_Click"/>
</StackPanel>
</Border>
<!-- 底部区域 -->
<Border Grid.Row="2" Background="#F5F5F5" Padding="20" BorderBrush="#E0E0E0" BorderThickness="0,1,0,0">
<WrapPanel HorizontalAlignment="Center">
<Button Content="注册账户"
Background="Transparent"
BorderThickness="0"
Foreground="#2196F3"
Cursor="Hand"
Click="RegisterButton_Click"
Margin="0,0,20,0"/>
<Button Content="忘记密码?"
Background="Transparent"
BorderThickness="0"
Foreground="#2196F3"
Cursor="Hand"
Click="ForgotButton_Click"/>
</WrapPanel>
</Border>
</Grid>
</Window>
csharp
// LoginWindow.xaml.cs
using System;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
namespace WpfDemo
{
public partial class LoginWindow : Window
{
private string correctCaptcha = "";
public LoginWindow()
{
InitializeComponent();
GenerateCaptcha();
LoadSavedCredentials();
}
private void GenerateCaptcha()
{
// 生成4位随机验证码
const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789";
var random = new Random();
var captcha = new char[4];
for (int i = 0; i < 4; i++)
{
captcha[i] = chars[random.Next(chars.Length)];
}
correctCaptcha = new string(captcha);
CaptchaText.Text = correctCaptcha;
}
private void LoadSavedCredentials()
{
if (Properties.Settings.Default.RememberPassword)
{
UserNameBox.Text = Properties.Settings.Default.UserName;
// 注意:实际应用中密码不应明文存储
// 这里仅作演示,生产环境应使用安全存储
RememberCheckBox.IsChecked = true;
}
}
private void SaveCredentials()
{
if (RememberCheckBox.IsChecked == true)
{
Properties.Settings.Default.UserName = UserNameBox.Text;
Properties.Settings.Default.RememberPassword = true;
}
else
{
Properties.Settings.Default.UserName = "";
Properties.Settings.Default.RememberPassword = false;
}
Properties.Settings.Default.Save();
}
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
// 清除错误消息
ErrorMsg.Visibility = Visibility.Collapsed;
// 获取输入
string username = UserNameBox.Text.Trim();
string password = PasswordBox.Password;
string captcha = CaptchaBox.Text.Trim().ToUpper();
// 验证
if (string.IsNullOrEmpty(username))
{
ShowError("请输入用户名");
return;
}
if (string.IsNullOrEmpty(password))
{
ShowError("请输入密码");
return;
}
if (captcha != correctCaptcha)
{
ShowError("验证码错误");
GenerateCaptcha(); // 刷新验证码
CaptchaBox.Text = "";
return;
}
// 模拟登录验证(实际应调用 API)
if (username == "admin" && password == "123456")
{
SaveCredentials();
// 打开主窗口
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
// 关闭登录窗口
this.Close();
}
else
{
ShowError("用户名或密码错误");
}
}
private void ShowError(string message)
{
ErrorMsg.Text = message;
ErrorMsg.Visibility = Visibility.Visible;
}
private void RegisterButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("注册功能开发中...", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
private void ForgotButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("请联系管理员重置密码", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
1.9 常见错误与陷阱
错误1:忘记设置 x:Name
xml
<!-- ❌ 错误:无法在后台代码中访问控件 -->
<TextBox x:Name="UserNameBox"/> <!-- 必须要有 x:Name -->
错误2:绑定语法错误
xml
<!-- ❌ 错误 -->
<TextBox Text="{Binding UserName}"/> <!-- 正确 -->
<TextBox Text="{Binding Path=UserName}"/> <!-- Path 可省略 -->
错误3:布局容器嵌套过深
xml
<!-- ❌ 性能差 -->
<Grid>
<StackPanel>
<Grid>
<Border>
<...>
</...>
</Border>
</Grid>
</StackPanel>
</Grid>
<!-- ✅ 使用合适的容器 -->
<Grid>
<!-- 直接布局 -->
</Grid>
错误4:硬编码尺寸
xml
<!-- ❌ 不利于响应式 -->
<Button Width="200" Height="50" />
<!-- ✅ 使用相对布局 -->
<Button HorizontalAlignment="Stretch" Height="Auto" />
<Button Width="Auto" Margin="10" />
1.10 练习题
基础题
-
创建一个 WPF 应用,使用 Grid 布局设计一个计算器界面。
-
设计一个注册表单,包含姓名、邮箱、密码、确认密码、性别、生日等字段,添加验证。
-
为应用创建一个统一的样式库,定义按钮、文本框、标签等控件的样式。
应用题
-
实现一个简单的待办事项应用:
-
ListBox 显示待办列表
-
TextBox 输入新待办
-
按钮添加/删除
-
复选框标记完成
-
-
创建一个图片查看器:
-
ListBox 显示图片列表
-
点击缩略图显示大图
-
支持缩放、旋转功能
-