WPF

技术栈
前端框架
wpfc#xamlwindows桌面应用毕设

概览

WPF 技术栈概览

WPF(Windows Presentation Foundation)是微软推出的Windows 桌面 UI 框架,基于 .NET,使用 XAML 标记语言描述界面。2006 年随 .NET Framework 3.0 发布,至今仍是 Windows 桌面开发的主流选择。

解决什么问题

  • Windows 桌面应用开发:企业管理系统、数据可视化工具、毕设客户端
  • 数据密集型界面:DataGrid、图表、数据绑定简化大量数据的展示与交互
  • 自定义控件/皮肤:高度可定制的 UI,支持动画、样式、模板

关键特性

  • XAML 声明式 UI:界面与逻辑分离,设计师和开发者可并行工作
  • MVVM 模式:数据绑定 + 命令,天然支持 Model-View-ViewModel 架构
  • 矢量渲染:基于 DirectX,界面缩放不失真,支持 3D
  • 丰富的控件:DataGrid、ListView、TreeView、Ribbon 等
  • 样式与模板:ControlTemplate、DataTemplate、Style 实现高度定制
  • .NET 生态兼容:可复用所有 .NET 库,NuGet 包丰富

安装

WPF 安装指南

1. 环境准备

要求 说明
操作系统 Windows 10 版本 1809+(WPF 仅支持 Windows)
IDE Visual Studio 2022 Community(免费)
.NET SDK .NET 6.0+ / .NET 8.0(推荐)
内存 建议 8GB+

2. 安装命令

安装 Visual Studio 2022

  1. 下载 Visual Studio Community
  2. 安装时勾选工作负载:
    • .NET 桌面开发(含 WPF、WinForms)
    • 可选:.NET Multi-platform App UI 开发
  3. 建议勾选单个组件:.NET 8.0 Runtime

验证安装

打开 Developer Command Prompt for VS 2022

dotnet --version
# 输出: 8.0.x

创建第一个 WPF 项目

dotnet new wpf -n MyWpfApp
cd MyWpfApp
dotnet run

3. 常见安装问题

问题 解决方案
WPF 项目模板不存在 VS Installer → 修改 → 勾选「.NET 桌面开发」
XAML 设计器不显示 确保 VS 版本 ≥ 2022 17.4,或使用「热重载」
System.Windows 找不到 检查项目 .csproj<UseWPF>true</UseWPF>
运行时闪退 查看「输出」窗口或事件查看器找异常信息
.NET 版本不匹配 dotnet --list-sdks 查看已安装版本,项目文件 <TargetFramework> 对应

示例

WPF MVVM 数据绑定——学生成绩管理

目标

展示 WPF 中 MVVM 模式的核心用法:数据绑定、命令、INotifyPropertyChanged,做一个简易学生成绩录入界面。

完整代码

项目结构

StudentManager/
├── Models/Student.cs
├── ViewModels/MainViewModel.cs
├── Views/MainWindow.xaml
├── Views/MainWindow.xaml.cs
├── App.xaml
└── App.xaml.cs

Models/Student.cs

namespace StudentManager.Models;

public class Student
{
    public string Name { get; set; } = "";
    public int Score { get; set; }
}

ViewModels/MainViewModel.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using StudentManager.Models;

namespace StudentManager.ViewModels;

public class MainViewModel : INotifyPropertyChanged
{
    private string _newName = "";
    private int _newScore;

    public string NewName
    {
        get => _newName;
        set { _newName = value; OnPropertyChanged(); }
    }

    public int NewScore
    {
        get => _newScore;
        set { _newScore = value; OnPropertyChanged(); }
    }

    public ObservableCollection<Student> Students { get; } = new();

    // 命令:添加学生
    public ICommand AddStudentCommand { get; }

    public MainViewModel()
    {
        AddStudentCommand = new RelayCommand(AddStudent, CanAddStudent);
    }

    private bool CanAddStudent() =>
        !string.IsNullOrWhiteSpace(NewName) && NewScore >= 0 && NewScore <= 100;

    private void AddStudent()
    {
        Students.Add(new Student { Name = NewName, Score = NewScore });
        NewName = "";
        NewScore = 0;
    }

    // INotifyPropertyChanged 实现
    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? name = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

// 简易 RelayCommand
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;
    public RelayCommand(Action execute, Func<bool> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
    public bool CanExecute(object? p) => _canExecute();
    public void Execute(object? p) => _execute();
    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }
}

Views/MainWindow.xaml

<Window x:Class="StudentManager.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:StudentManager.ViewModels"
        Title="学生成绩管理" Height="400" Width="500">

    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>

    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- 输入区域 -->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
            <TextBlock Text="姓名:" VerticalAlignment="Center" Margin="0,0,5,0"/>
            <TextBox Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}"
                     Width="120" Margin="0,0,10,0"/>
            <TextBlock Text="成绩:" VerticalAlignment="Center" Margin="0,0,5,0"/>
            <TextBox Text="{Binding NewScore, UpdateSourceTrigger=PropertyChanged}"
                     Width="60" Margin="0,0,10,0"/>
            <Button Content="添加" Command="{Binding AddStudentCommand}"
                    Width="60"/>
        </StackPanel>

        <!-- 统计 -->
        <TextBlock Grid.Row="1" Margin="0,0,0,8">
            <Run Text="学生人数: "/>
            <Run Text="{Binding Students.Count}" FontWeight="Bold"/>
        </TextBlock>

        <!-- 数据表格 -->
        <DataGrid Grid.Row="2" ItemsSource="{Binding Students}"
                  AutoGenerateColumns="False" IsReadOnly="True">
            <DataGrid.Columns>
                <DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*"/>
                <DataGridTextColumn Header="成绩" Binding="{Binding Score}" Width="80"/>
                <DataGridTextColumn Header="等级" Width="80"
                    Binding="{Binding Score, Converter={StaticResource ScoreConverter}}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

App.xaml(含 ScoreConverter)

<Application x:Class="StudentManager.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:StudentManager">
    <Application.Resources>
        <local:ScoreConverter x:Key="ScoreConverter"/>
    </Application.Resources>
</Application>

ScoreConverter(分数→等级)

using System.Windows.Data;
using System.Globalization;

namespace StudentManager;

public class ScoreConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is int score)
            return score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
        return "F";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        => throw new NotImplementedException();
}

运行步骤

dotnet new wpf -n StudentManager
# 将上述代码分别放入对应文件
dotnet run

关键点

  • DataContext 是 MVVM 的桥梁,View 通过 Binding 与 ViewModel 通信
  • INotifyPropertyChanged:属性变化时自动通知 UI 更新
  • ObservableCollection:集合增删自动刷新 DataGrid
  • ICommand:将按钮操作绑定到 ViewModel 方法,避免 Code-Behind
  • IValueConverter:把数据值转换为 UI 展示值(分数→等级)

教程

WPF 毕设入门教程——Windows 桌面开发

前言

WPF 是 Windows 桌面开发的高配选择,适合毕设中需要复杂界面的项目:教务管理系统、销售管理、数据看板等。


第一章:MVVM 模式理解

MVVM = Model - View - ViewModel,是 WPF 的灵魂模式。

View (XAML)  ←→  ViewModel (C#)  ←→  Model (数据类)
  绑定                属性/命令            数据

为什么 MVVM?

  • View 零代码:XAML 只写界面,不写逻辑
  • 可测试:ViewModel 不依赖 UI,可单元测试
  • 解耦:设计师改界面不影响逻辑

第二章:数据绑定速成

基础绑定

<TextBlock Text="{Binding StudentName}" />

双向绑定(输入框)

<TextBox Text="{Binding StudentName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

集合绑定

<ListBox ItemsSource="{Binding Students}"
         DisplayMemberPath="Name" />

数据上下文

// Code-Behind 或 XAML
this.DataContext = new MainViewModel();

第三章:毕设实战——图书管理系统界面

主界面布局(XAML 骨架)

<Window ...>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>   <!-- 侧边栏 -->
            <ColumnDefinition Width="*"/>      <!-- 主内容 -->
        </Grid.ColumnDefinitions>

        <!-- 导航菜单 -->
        <ListBox Grid.Column="0" ItemsSource="{Binding MenuItems}" />

        <!-- 内容区:根据选择切换 -->
        <ContentControl Grid.Column="1"
                        Content="{Binding SelectedViewModel}" />
    </Grid>
</Window>

ViewModel 核心代码

public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Book> Books { get; } = new();

    private Book? _selectedBook;
    public Book? SelectedBook
    {
        get => _selectedBook;
        set { _selectedBook = value; OnPropertyChanged(); }
    }

    // 搜索命令
    public ICommand SearchCommand { get; }

    public MainViewModel()
    {
        SearchCommand = new RelayCommand(async () =>
        {
            var results = await _bookService.SearchAsync(Keyword);
            Books.Clear();
            foreach (var b in results) Books.Add(b);
        });
    }
}

第四章:样式与美化

Material Design 集成(毕设颜值加分)

dotnet add package MaterialDesignThemes
<Window xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
        Background="{DynamicResource MaterialDesignPaper}">
    <Button Style="{StaticResource MaterialDesignRaisedButton}"
            Content="搜索" />
</Window>

第五章:毕设常见问题

问题 解决
数据不更新 检查是否实现了 INotifyPropertyChanged
DataGrid 列不显示 设置 AutoGenerateColumns="False" 并手动定义列
窗口打开慢 考虑使用异步初始化,或启动画面(Splash Screen)
发布后缺 DLL 发布模式选「独立」,包含 .NET 运行时

思考题

  1. MVVM 中的 ICommand 和传统 Click 事件有什么本质区别?
  2. 如何实现 WPF 中的页面导航(类似浏览器前进/后退)?
  3. DependencyProperty 和普通 CLR 属性有什么区别?何时使用?