C# 零基础到精通教程 - WPF 专题一:WPF 入门与 XAML 基础

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

  1. 访问 visualstudio.microsoft.com

  2. 下载 Visual Studio 2022 Community(免费)

  3. 安装时勾选 .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:

  1. 文件 → 新建 → 项目

  2. 搜索 "WPF" → 选择 WPF 应用程序

  3. 设置项目名称和位置 → 创建

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 练习题

基础题

  1. 创建一个 WPF 应用,使用 Grid 布局设计一个计算器界面。

  2. 设计一个注册表单,包含姓名、邮箱、密码、确认密码、性别、生日等字段,添加验证。

  3. 为应用创建一个统一的样式库,定义按钮、文本框、标签等控件的样式。

应用题

  1. 实现一个简单的待办事项应用:

    • ListBox 显示待办列表

    • TextBox 输入新待办

    • 按钮添加/删除

    • 复选框标记完成

  2. 创建一个图片查看器:

    • ListBox 显示图片列表

    • 点击缩略图显示大图

    • 支持缩放、旋转功能

相关推荐
兆。1 小时前
LangChain文档处理集成指南:面向知识管理开发者
开发语言·langchain·c#
_oP_i2 小时前
105、word 出现 {TOCO“1-2“HZ}
开发语言·c#·word
qq_431280702 小时前
生成解决方案将文件生成到根目录或指定文件夹下
wpf
影寂ldy2 小时前
C#构造函数 + 析构函数
开发语言·c#
影寂ldy18 小时前
C# 类和对象
开发语言·c#
z落落20 小时前
C# 构造函数(无参/有参/重载/this)+析构函数(终结器)|GC 垃圾回收
java·开发语言·c#
z落落20 小时前
C# 字段与属性(get/set访问器、三种属性写法、只读属性)+属性拦截例子(get动态计算 + set数据校验)
开发语言·c#
影寂ldy20 小时前
C#栈和队列
开发语言·c#
魔法阵维护师21 小时前
从零开发游戏需要学习的c#模块,第三十四章(设置界面)
学习·游戏·c#