AIGC标识 1阶段2章笔记 01 WPF项目创建与程序结构了解

第一阶段 · 第二章:WPF 项目创建与程序结构了解

上一篇我们搭好了 Visual Studio 2026 的环境。这一章做两件事:创建一个真正的 WPF 项目,以及看懂这个项目里每个文件都是干嘛的。


一、项目创建:三步走

第 1 步:选模板

打开 Visual Studio,选择 "创建新项目",在搜索框输入 WPF,然后把筛选条件依次设为:

  • 语言:C#
  • 平台:Windows
  • 项目类型:桌面

在筛选后的列表里选中 "WPF 应用程序"(描述:用于创建 .NET WPF 应用程序的项目),点击"下一步"。

选择 WPF 应用程序模板

⚠️ 最容易踩的坑

列表里还有一个叫 WPF 应用(.NET Framework) 的模板,它和我们要选的只差一个括号,但完全是两回事:

模板 运行框架 现状
WPF 应用程序 现代 .NET(.NET 8 / .NET 10) ✅ 选这个,持续更新,能用到新特性
WPF 应用(.NET Framework) .NET Framework 4.x ❌ 旧框架,仅维护,无法升级到现代 .NET

一旦选错,后期想改基本等于重来。判断依据:选中后点"下一步",下一页框架下拉框里显示的应该是 .NET x.0,而不是 .NET Framework 4.x。

第 2 步:配置项目名称与位置

进入"配置新项目"页,需要填三个东西:

项目 示例值 说明
项目名称 App1 一个项目(一个程序)的名字
位置 D:\Code\WPF\ 解决方案存放的文件夹
解决方案名称 WpfApp 整个解决方案的名字

配置新项目

关于"将解决方案和项目放在同一目录中"这个选项,一般不勾选。

原因是它决定了磁盘上的目录层级:

  • 不勾选(推荐):WpfApp\ 文件夹 → 里面有 .sln 文件 + App1\ 子文件夹 → 子文件夹里才是项目文件。
    D:\Code\WPF\WpfApp\          ← 解决方案文件夹
        WpfApp.sln                ← 解决方案文件
        App1\                     ← 项目文件夹
            App1.csproj
            App.xaml
            MainWindow.xaml
    
    这样当一个解决方案里放进第二个、第三个项目时,每个项目各占一个平级文件夹,一目了然。
  • 勾选:.sln 和 .csproj 挤在同一层。单项目时看着清爽,一旦加第二个项目就会变得混乱。

💡 命名小贴士:项目名和解决方案名尽量用英文(如 WpfApp、App1),路径也尽量避免中文和空格,能省掉后续一堆莫名其妙的编译和打包问题。

第 3 步:选择框架

最后一页是"其他信息",核心是 框架(Framework) 下拉框,没有特殊要求时保持默认即可。

选择框架

以本次截图为例,默认是 .NET 8.0(长期支持)。

关于版本,记住两点就够了:

  • LTS(长期支持):官方标注"(长期支持)"的版本,约支持三年,学习与生产都优先选它;
  • STS(标准支持):支持周期较短,适合想尝鲜新特性的场景。

你的 Visual Studio 装了哪些 .NET 版本,这里就列出哪些。如果安装时一并勾选了 .NET 10 组件,这里同样会出现 .NET 10.0(长期支持),选它也没有问题。

点击 "创建",第一个 WPF 项目就诞生了。


二、程序结构:一个项目由哪几部分组成

创建完成后,把视线移到右侧(或左侧)的 解决方案资源管理器,一个 WPF 项目的全貌就在这里。

解决方案资源管理器中的项目结构

从结构上看,一个 WPF 项目由 四部分 组成:

App1(项目)
├── 依赖项                ← 这个项目"引用了什么"
├── App.xaml + App.xaml.cs        ← 应用程序级别(整个程序)
├── MainWindow.xaml + MainWindow.xaml.cs   ← 窗口级别(一个界面)
└── AssemblyInfo.cs      ← 程序集特性配置

2.1 依赖项

"依赖项"节点不是磁盘上的真实文件夹,它只是一个逻辑分组,告诉你这个项目用到了哪些外部东西。展开后主要会看到:

子节点 含义
框架(Framework) 最关键的是 Microsoft.WindowsDesktop.App.WPF。它是 WPF 的运行基础,装 VS 时就带上了,不需要自己下载,也别去 NuGet 里手动装
分析器(Analyzers) 代码检查工具,编译时会提示潜在问题,不用管它
包(Packages) 通过 NuGet 安装的第三方库,装了才会出现
项目(Projects) 引用同一解决方案里的其他项目,引用了才会出现

编译器报错提示"缺少某某命名空间"时,八成就是这里的依赖项没配好。

2.2 App.xaml —— 整个程序的"总控"

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

它是整个应用程序的入口与配置,可以理解成程序的"开机设置页"。重点看三个地方:

  • x:Class="App1.App":告诉编译器"这个 XAML 对应的后台类是 App1 命名空间下的 App 类"。XAML 和 .cs 文件就是靠这一行配对的,改类名时两边必须同步改,否则编译报错。
  • StartupUri="MainWindow.xaml":程序启动后第一个显示哪个窗口。想让程序启动时打开登录窗口,把这里改成 LoginWindow.xaml 就行——这是后面做多窗口切换时最常用的一个属性。
  • <Application.Resources>:全局资源区。放在这里的样式、模板、画刷,整个程序的所有窗口都能用。比如你想让所有按钮统一长一个样,样式就写在这里。

开头那几行 xmlns=... 是命名空间声明,不是网址,不会联网。它们的作用是"给一段类型起个别名":

  • 不带前缀的默认 xmlns:WPF 的核心控件都在这里(Button、Grid、Window…),所以写 <Grid> 不用加前缀;
  • xmlns:x:XAML 自身的关键字,凡是 x:Class、x:Name 这类带 x: 前缀的都来自它;
  • xmlns:local:指向本项目自己的命名空间 App1,这样在 XAML 里就能直接用自己写的类。

2.3 App.xaml.cs —— 程序的后台逻辑

using System.Windows;

namespace App1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
    }
}

三个关键点:

  1. partial(分部类):App 这个类被拆成了两半——一半是 XAML 编译生成的,一半就是你眼前这个 .cs 文件。两边合起来才是完整的 App 类。这也是 x:Class 必须和这里对上的原因。
  2. 继承自 Application:WPF 中所有应用程序的基类,程序的生命周期都由它管。
  3. 这里看不到 Main 方法,也看不到构造函数:因为它们是编译时自动生成的(在 obj 文件夹下的 App.g.cs 里)。生成的代码大致做了这么几件事:创建 App 对象 → 调用 InitializeComponent() 加载 App.xaml → Run() 启动消息循环。这就是为什么我们一个字没写,程序也能跑起来。

补充:笔记里出现的 using System.Configuration; 和 using System.Data; 属于旧框架时代的遗留写法,现代 .NET 的 WPF 模板默认并不生成它们。如果编译时提示找不到,直接删掉即可,不影响程序运行。

App.xaml.cs 后面最常用的三个扩展点,先混个眼熟:

写法 用途
重写 OnStartup 程序启动时要做的事(读配置、初始化数据库连接、显示登录窗)
重写 OnExit 程序退出时要做的事(保存状态、写日志)
Application.Current 在任意位置拿到当前这个应用程序对象

2.4 MainWindow.xaml —— 一个窗口的界面

<Window x:Class="App1.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:App1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

    </Grid>
</Window>
  • Window:WPF 的窗口,程序里看到的那个能拖动能关闭的方框就是它。
  • Title="MainWindow":标题栏文字;Height="450" Width="800":初始高宽。注意 WPF 里的单位不是屏幕像素,而是与设备无关的单位(1/96 英寸),所以在高分屏上不会像老程序那样糊掉、缩成一团——这正是 WPF 做界面的优势之一。
  • <Grid>:默认给的根布局容器。Window 只能有一个直接子元素,所以通常放一个 Grid,然后在 Grid 里再摆成百上千个控件。
  • xmlns:d 和 mc:Ignorable="d":这一对是设计时专用的。d: 前缀的属性(如 d:DesignHeight)只在 Visual Studio 设计器里起作用,用来预览效果;mc:Ignorable="d" 则告诉编译器"看到 d: 开头的就忽略掉",不会编进最终程序。新手完全可以无视它,别被这两行吓到。

2.5 MainWindow.xaml.cs —— 窗口的后台逻辑

namespace App1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

全文最需要记住的只有一行:InitializeComponent();

它的作用是"把 XAML 翻译成真正的界面":读取 MainWindow.xaml,创建出 Grid、Button 等控件对象,安排好层级关系,绑好事件,并给带 x:Name 的控件生成对应的字段。

⚠️ 千万不要删掉它。 一旦删掉,编译也许不报错,但运行时界面一片空白,而且代码里访问任何控件都会得到 null(空引用异常)。这是初学者最经典的"为什么我的按钮是 null"事故源头。

顶部那一长串 using System.Windows.Controls;、using System.Windows.Shapes; 之类,是模板预先把 WPF 常用命名空间都引好了,用到哪个算哪个,不用管。

2.6 AssemblyInfo.cs —— 程序集特性

using System.Windows;

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None,            // 主题专用资源字典的位置
    ResourceDictionaryLocation.SourceAssembly   // 通用资源字典的位置
)]

这个文件声明的是程序集级别的特性,当前唯一的内容是 ThemeInfo,它告诉 WPF:本程序没有按 Windows 主题(浅色/深色)分开的资源字典,通用资源就在本程序集里找。

它是 WPF 项目特有的配置,学习阶段保持原样、完全不用动。真正写样式、做主题时才会回头来改它。

2.7 XAML 与 .xaml.cs 是怎么"配对"的

这是理解 WPF 结构的关键,用一张图说清:

MainWindow.xaml          ←→      MainWindow.xaml.cs
  x:Class="App1.MainWindow"         partial class MainWindow : Window
        │                                      │
        └──────── 编译时合并为同一个类 ──────────┘
                         ↓
              obj\...\MainWindow.g.cs(自动生成)
                  里面实现了 InitializeComponent()

要点三连:

  1. x:Class 的值 = 后台类的 命名空间 + 类名;
  2. 后台类必须标 partial;
  3. InitializeComponent() 由编译器生成,不是你写的,但你必须调用它。

2.8 资源管理器里看不到的两个文件夹

解决方案资源管理器显示的是逻辑视图,并不等于磁盘上的真实文件。项目文件夹里还有两个重要目录被默认隐藏了:

文件夹 作用
bin 编译输出。生成的 .exe 在这里:bin\Debug\net8.0-windows\App1.exe,双击就能直接运行程序
obj 编译中间产物,App.g.cs、MainWindow.g.cs 这些自动生成的文件都在这里

想看它们,点一下解决方案资源管理器顶部的 "显示所有文件" 按钮即可。(obj 里的东西不要手改,下次编译会被覆盖。)


三、解决方案与项目的关系

一个解决方案(Solution)可以包含多个项目(Project),关系是 1 对多。

解决方案中的多个项目

上图这个 WpfApp 解决方案里就同时放着 App1 和 App2 两个 WPF 项目——每个都有自己的 App.xaml、MainWindow.xaml,彼此独立,互不干扰。

关于它们的本质,可以这么理解:

  • 从 Visual Studio 的角度看:解决方案的本质是 .sln 文件,项目的本质是 .csproj 文件。这两个文件才是它们的"身份证",里面记录了"我包含哪些项目""我有哪些文件、引用了什么"。删掉这两个文件,VS 就认不出它们了。
  • 从磁盘的角度看:解决方案对应一个文件夹,项目也对应一个子文件夹,项目文件夹里放着该项目的所有文件。所以我们能在一个解决方案文件夹里,放多个代表项目的文件夹。
D:\Code\WPF\WpfApp\        ← 解决方案(文件夹 + WpfApp.sln)
    ├── App1\              ← 项目1(文件夹 + App1.csproj)
    └── App2\              ← 项目2(文件夹 + App2.csproj)

顺带纠正一个常见说法:与其说"本质是文件夹",不如说"本质是 .sln / .csproj 文件,它们在磁盘上以文件夹的形式组织"。理解到 .sln 和 .csproj 这一层,后面手动改项目文件、排查引用错误时会轻松很多。

启动项目:一次能启动几个?

一个解决方案里有多个项目,按 F5 时运行哪一个?这就是启动项目(也叫启动项)。

  • 默认情况:一个解决方案同一时间只有一个启动项目;
  • 怎么切换:最省事的办法是用工具栏上的启动项下拉框直接选。

设置启动项目

另外两种常用方式:

  1. 在解决方案资源管理器里右键某个项目 → "设为启动项目";
  2. 右键解决方案 → "属性"(或下拉框里的"配置启动项目...")→ 可以设置多个项目同时启动,也可以选择"当前选定内容"(选中哪个项目就运行哪个,调试多项目时非常方便)。

启动项目的名字在工具栏里会显示为加粗,并且下拉列表中带一个对勾,一眼就能看出当前跑的是谁。


四、本章小结

创建项目的三步:选模板(认准不带 .NET Framework 的那个)→ 填名称与位置(默认不勾选"放在同一目录")→ 框架选默认的 LTS 版本。

项目结构的四部分:

组成部分 一句话记住
依赖项 项目用了哪些外部东西,WPF 框架在这里,不用手动装
App.xaml(.cs) 整个程序的总控:StartupUri 决定先开哪个窗,Resources 放全局资源
MainWindow.xaml(.cs) 一个窗口的界面与逻辑,InitializeComponent() 是灵魂,不能删
AssemblyInfo.cs 程序集特性,当前只有 ThemeInfo,保持原样不动

两个原理:XAML 与 .xaml.cs 靠 x:Class + partial 配对;InitializeComponent() 由编译自动生成。

一组关系:解决方案(.sln): 项目(.csproj)= 1 : 多,同一时刻默认只有一个启动项目,可在工具栏一键切换。


本文为个人学习笔记,如有理解偏差之处,欢迎指正交流。

posted @ 2026-09-29 00:57  smonkey1257  阅读(4)  评论(0)    收藏  举报