第一部分:WPF 核心知识 #
1. 什么是 WPF?和 WinForms 有什么区别? #
WPF 是基于 DirectX 的桌面 UI 框架,使用 XAML 构建界面。
| 对比项 | WPF | WinForms |
|---|---|---|
| 渲染引擎 | DirectX | GDI/GDI+ |
| 界面描述 | XAML + C# | 纯 C# / 设计器 |
| 数据绑定 | 强大 | 较弱 |
| 样式模板 | 支持 | 基本不支持 |
| 动画 | 支持 | 基本不支持 |
| 架构模式 | 适合 MVVM | 事件驱动 |
2. WPF 的核心特点 #
- XAML
- 数据绑定
- 依赖属性
- 路由事件
- 样式与模板
- 动画
- MVVM
3. 什么是 XAML? #
- XAML 是一种声明式标记语言,用来描述 UI。
- 本质上会被编译成 BAML,再由 WPF 加载生成对象树。
- 优点是 界面和业务逻辑分离。
4. 什么是逻辑树(Logical Tree)和视觉树(Visual Tree)? #
- 逻辑树:描述控件之间的逻辑包含关系,比如 Window -> Grid -> Button。
- 视觉树:描述控件最终渲染出来的完整结构,包含模板生成的内部元素。
- 查找控件、事件路由、样式应用时常会涉及这两个概念。
5. 什么是依赖属性(DependencyProperty)? #
WPF 特有属性系统,不只是普通 CLR 属性。
支持:
- 数据绑定
- 样式
- 动画
- 默认值
- 属性值继承
- 变更通知
常用于自定义控件。
6. 依赖属性和普通属性有什么区别? #
- 普通属性:只是字段封装。
- 依赖属性:由 WPF 属性系统管理,可参与绑定、动画、样式等。
- 一般控件公开可绑定的属性,优先考虑依赖属性。
7. 如何注册一个依赖属性? #
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
"Title",
typeof(string),
typeof(MyControl),
new PropertyMetadata("默认值"));
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}8. 什么是附加属性(Attached Property)? #
- 某个类定义的属性,可以"附加"到别的对象上使用。
- 典型例子:
Grid.Row、Canvas.Left - 本质上也是依赖属性。
9. 什么是路由事件(RoutedEvent)? #
事件可以在元素树中传播,不只是当前控件自己处理。
三种类型:
- 冒泡(Bubbling):从源控件向父级传播,如
Click - 隧道(Tunneling):从根向源控件传播,一般以
Preview开头,如PreviewMouseDown - 直接(Direct):只在当前控件触发
10. 什么是数据绑定(Binding)? #
- 将 UI 和数据对象连接起来。
- UI 显示数据、用户修改数据时可同步到对象。
- 是 WPF 和 MVVM 的核心。
11. 常见 Binding Mode 有哪些? #
| 模式 | 说明 |
|---|---|
OneWay |
数据源 -> UI |
TwoWay |
双向同步 |
OneTime |
只绑定一次 |
OneWayToSource |
UI -> 数据源 |
Default |
使用控件默认模式 |
常见追问:
TextBox.Text默认是 TwoWay- 但
UpdateSourceTrigger默认通常是 LostFocus
12. 什么是 DataContext? #
- DataContext 是绑定时默认的数据源。
- 它会沿着视觉/逻辑树向下继承。
- 子元素没显式指定 Source 时,会自动使用 DataContext。
13. Binding 中 Source、ElementName、RelativeSource 的区别? #
| 属性 | 说明 |
|---|---|
Source |
手动指定数据源对象 |
ElementName |
绑定到某个控件 |
RelativeSource |
绑定到相对位置对象,如自己、父元素、TemplatedParent |
14. UpdateSourceTrigger 有哪些? #
| 值 | 说明 |
|---|---|
PropertyChanged |
属性变化立即更新 |
LostFocus |
失去焦点更新 |
Explicit |
手动更新 |
15. 什么是 INotifyPropertyChanged? #
当对象属性变化时通知 UI 刷新。ViewModel 基本都会实现它。
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}16. ObservableCollection 和 List 的区别? #
| 类型 | 特点 |
|---|---|
List<T> |
集合变化不会自动通知 UI |
ObservableCollection<T> |
增删集合项时会通知 UI 刷新 |
注意: ObservableCollection 只通知集合变化,不通知集合中对象属性变化。对象属性变化还要靠 INotifyPropertyChanged。
17. 什么是 ICommand? #
用来替代按钮点击事件,适合 MVVM。
核心成员:
ExecuteCanExecuteCanExecuteChanged
18. 为什么 MVVM 中推荐用 Command,而不是 Click 事件? #
- 事件会让 View 和代码后置耦合
- Command 更符合 MVVM,便于测试和维护
- 按钮是否可用可以通过
CanExecute自动控制
19. 什么是 MVVM? #
| 层 | 职责 |
|---|---|
| Model | 数据模型 |
| View | 界面 |
| ViewModel | 界面逻辑和状态 |
通过绑定和命令连接 View 与 ViewModel,减少 UI 和业务耦合。
20. MVVM 的优点是什么? #
- 界面和逻辑解耦
- 更易测试
- 更易维护
- 更适合多人协作
- 符合 WPF 的绑定机制
21. Style、ControlTemplate、DataTemplate 的区别? #
| 类型 | 作用 |
|---|---|
Style |
设置控件属性、统一外观 |
ControlTemplate |
重定义控件外观结构,不改变数据 |
DataTemplate |
定义数据对象如何显示 |
一句话记忆:
- Style 改属性
- ControlTemplate 改控件长相
- DataTemplate 改数据展示
22. StaticResource 和 DynamicResource 的区别? #
| 类型 | 特点 |
|---|---|
StaticResource |
加载时解析,性能更好 |
DynamicResource |
运行时查找,资源变化后可动态更新 |
一般静态资源优先用 StaticResource。
23. Trigger、DataTrigger、EventTrigger 的区别? #
| 类型 | 触发条件 |
|---|---|
Trigger |
基于控件属性值 |
DataTrigger |
基于绑定数据值 |
EventTrigger |
基于事件,常配合动画 |
24. Converter 有什么用? #
在绑定过程中进行数据转换。
例如:
- bool -> Visibility
- 数字 -> 颜色
- 枚举 -> 文本
接口:
IValueConverterIMultiValueConverter
25. ItemsControl、ListBox、ListView 的区别? #
| 控件 | 特点 |
|---|---|
ItemsControl |
最基础的项控件,只负责显示项 |
ListBox |
支持选择 |
ListView |
更强展示能力,常配合 GridView |
26. UserControl 和 CustomControl 的区别? #
| 类型 | 适用场景 |
|---|---|
UserControl |
快速组合现有控件,适合业务界面 |
CustomControl |
适合做可复用控件库,支持模板和主题 |
面试标准答法:
- 业务页面用
UserControl - 通用控件库用
CustomControl
27. 什么是模板绑定(TemplateBinding)? #
- 在
ControlTemplate中绑定到控件自身属性。 - 比普通绑定更轻量。
- 常见于自定义控件模板中。
28. WPF 中 UI 线程问题怎么处理? #
WPF 大多数 UI 对象只能在创建它的线程访问,通常是主线程。后台线程更新 UI,要通过 Dispatcher 切回 UI 线程。
Dispatcher.Invoke(() =>
{
txtMessage.Text = "更新UI";
});29. Dispatcher.Invoke 和 BeginInvoke 的区别? #
| 方法 | 特点 |
|---|---|
Invoke |
同步执行,会阻塞当前线程 |
BeginInvoke |
异步执行,不阻塞 |
30. async/await 在 WPF 中有什么注意点? #
- 不要在 UI 线程上用
.Result或.Wait(),容易死锁 - 耗时操作放后台线程
- await 后默认会回到 UI 同步上下文,所以可以继续更新 UI
31. WPF 中常见内存泄漏原因有哪些? #
- 事件注册后没取消
- 静态事件
- 长生命周期对象持有短生命周期对象引用
- 绑定、计时器、消息订阅没释放
- 可以用
WeakEventManager降低事件引用问题
32. WPF 中如何做输入校验? #
常见方式:
ValidationRuleIDataErrorInfoINotifyDataErrorInfo- 异常校验
ValidatesOnExceptions=True
现在更推荐 INotifyDataErrorInfo,支持异步和多错误。
33. 什么是资源字典(ResourceDictionary)? #
用来存放样式、模板、画刷、字符串等资源。
可放在:
- App.xaml
- Window / UserControl
- 单独 xaml 文件
可通过 MergedDictionaries 合并多个资源文件。
34. WPF 中常见布局控件有哪些? #
| 控件 | 特点 |
|---|---|
Grid |
最常用,行列布局 |
StackPanel |
横向/纵向堆叠 |
DockPanel |
停靠布局 |
WrapPanel |
自动换行 |
Canvas |
绝对定位 |
UniformGrid |
等分网格 |
35. Grid 和 StackPanel 怎么选? #
- 复杂界面优先
Grid - 简单纵向/横向排列用
StackPanel StackPanel在滚动、拉伸场景下有时不如Grid灵活
36. 什么是视觉虚拟化(UI Virtualization)? #
- 只创建当前可见项的 UI 元素,提升性能。
- 大数据列表很重要。
- 常见依赖
VirtualizingStackPanel。
37. 如何提高 WPF 性能? #
- 使用 UI 虚拟化
- 减少视觉树层级
- 优先
StaticResource - 避免频繁刷新整个 UI
- 大量数据时分页/懒加载
- 耗时操作异步处理
- 少用不必要动画和复杂绑定
38. Show 和 ShowDialog 的区别? #
| 方法 | 特点 |
|---|---|
Show() |
非模态窗口 |
ShowDialog() |
模态窗口,会阻塞当前窗口交互,直到关闭 |
39. 如何在 WPF 中调试绑定错误? #
- 先看 Output 输出窗口
- 绑定错误通常会直接输出
- 常见问题:
- 属性名拼错
- DataContext 不对
- 路径错误
- 转换器异常
40. WPF 中 Loaded、Initialized、Unloaded 的理解? #
| 事件 | 说明 |
|---|---|
Initialized |
对象初始化完成 |
Loaded |
控件已加载到可视树,可开始交互 |
Unloaded |
从界面移除时触发 |
第二部分:C# 核心知识 #
41. 值类型和引用类型的区别? #
| 类型 | 特点 | 举例 |
|---|---|---|
| 值类型 | 直接存值 | int、struct、bool、enum |
| 引用类型 | 存对象引用 | class、string、数组 |
值类型通常分配在栈/内联,引用类型对象通常在堆上。
42. string 是值类型还是引用类型? #
string是 引用类型- 但它是不可变的(immutable)
43. string 和 StringBuilder 的区别? #
| 类型 | 特点 |
|---|---|
string |
不可变,频繁拼接性能差 |
StringBuilder |
适合大量字符串拼接 |
44. abstract 和 interface 的区别? #
| 对比项 | abstract | interface |
|---|---|---|
| 是否可有实现 | 可以 | C# 8.0 前不行 |
| 字段 | 可以有 | 不可以 |
| 构造函数 | 可以有 | 不可以 |
| 继承 | 单继承 | 多实现 |
| 语义 | “是什么” | “能做什么” |
45. virtual、override、new 的区别? #
| 关键字 | 说明 |
|---|---|
virtual |
父类声明可重写 |
override |
子类真正重写 |
new |
隐藏父类成员,不是重写 |
46. ref 和 out 的区别? #
| 关键字 | 说明 |
|---|---|
ref |
传入前必须赋值 |
out |
传入前不用赋值,方法内必须赋值 |
47. 委托和事件的区别? #
| 类型 | 特点 |
|---|---|
| 委托 | 方法引用类型 |
| 事件 | 对委托的封装,只能订阅/取消,不能直接外部触发 |
事件更安全,适合发布订阅模式。
48. Func、Action、Predicate 是什么? #
| 类型 | 说明 |
|---|---|
Action |
无返回值委托 |
Func |
有返回值委托 |
Predicate<T> |
返回 bool 的委托 |
49. 什么是装箱和拆箱? #
- 装箱:值类型 -> object
- 拆箱:object -> 值类型
- 会有性能开销,应尽量避免频繁装拆箱
50. 什么是泛型?有什么好处? #
让代码支持多种类型而不牺牲类型安全。
好处:
- 复用
- 类型安全
- 减少装箱拆箱
- 性能更好
51. using 的作用是什么? #
- 用于命名空间导入
- 也可用于资源释放,确保调用
Dispose()
52. IDisposable 有什么用? #
用来释放非托管资源或重要托管资源。常见如文件流、数据库连接、定时器等。
53. finalizer(析构函数)和 Dispose 的区别? #
| 类型 | 特点 |
|---|---|
Dispose |
手动、确定性释放 |
| 析构函数 | GC 不确定时间调用,成本高 |
推荐优先 Dispose。
54. Thread、Task、async/await 的区别? #
| 类型 | 特点 |
|---|---|
Thread |
底层线程 |
Task |
对异步操作的更高层封装 |
async/await |
简化异步编程语法 |
现代开发更常用 Task + async/await。
55. 什么是死锁?在 UI 中怎么出现? #
两个或多个操作互相等待,导致永远卡住。
WPF 常见是 UI 线程里调用异步任务的 .Result/.Wait() 导致死锁。
56. LINQ 的延迟执行是什么意思? #
- 查询语句定义后不立即执行,真正枚举时才执行。
- 如
Where、Select常是延迟执行。 ToList()、ToArray()会立即执行。
57. var、dynamic、object 的区别? #
| 类型 | 特点 |
|---|---|
var |
编译期推断类型,本质仍是强类型 |
object |
所有类型基类,调用成员需强转 |
dynamic |
运行时解析,编译器不过多检查,灵活但不安全 |
58. == 和 Equals 的区别? #
==:默认比较引用,部分类型会重载成值比较Equals:一般用于比较对象逻辑相等性- 面试回答要看具体类型实现
第三部分:WPF 高频场景题 #
59. 为什么界面不刷新? #
常见原因:
- 属性没实现
INotifyPropertyChanged - 集合用了
List而不是ObservableCollection - DataContext 不对
- 绑定路径写错
- 后台线程直接更新 UI 导致异常
60. 为什么按钮 CanExecute 改了,但界面没更新? #
- 没触发
CanExecuteChanged - 或没调用命令重查询逻辑
- 若用 RelayCommand,需要主动 RaiseCanExecuteChanged
61. DataGrid/ListBox 数据很多卡顿怎么办? #
- 开启虚拟化
- 分页加载
- 减少复杂模板
- 避免每项都做重计算
- 不要频繁重设
ItemsSource
62. 为什么 TextBox 改值后,ViewModel 没更新? #
- 绑定模式可能不是
TwoWay UpdateSourceTrigger可能是LostFocus- 属性没有
set - DataContext 不对
63. ObservableCollection 为什么能更新列表,但项内容变化不刷新? #
因为它只通知"集合变化",不通知"项属性变化"。每个项对象还需要实现 INotifyPropertyChanged。
64. UserControl 中 DataContext 为什么容易出问题? #
因为 UserControl 里如果自己设置 DataContext = this,会覆盖外部传入的 DataContext。正确做法:谨慎设置,必要时用 RelativeSource 或依赖属性传值。
65. 什么时候用 DependencyProperty,什么时候用 INotifyPropertyChanged? #
| 场景 | 选择 |
|---|---|
| 控件开发、需要样式/动画/绑定支持 | DependencyProperty |
| ViewModel 属性通知 | INotifyPropertyChanged |
66. 什么时候用事件,什么时候用命令? #
- MVVM 中优先用 命令
- 控件内部行为、底层交互、复杂路由事件可用 事件
67. StaticResource 用得好好的,什么时候必须用 DynamicResource? #
主题切换、运行时资源变更、皮肤切换等需要动态刷新的场景。
68. 为什么会选择 MVVM? #
为了降低 View 和业务逻辑耦合,提高可维护性、可测试性和复用性。
第四部分:面试高频短答速记 #
69. WPF 绑定失败首先看什么? #
看 Output 输出窗口。
70. TextBox.Text 默认绑定模式是什么? #
默认 TwoWay,默认 UpdateSourceTrigger 通常是 LostFocus。
71. Grid.Row 是什么? #
附加属性。
72. Button.Click 属于什么事件? #
路由事件,一般是 冒泡事件。
73. PreviewMouseDown 属于什么事件? #
隧道事件(Tunneling)。
74. DataTemplate 用于什么? #
定义"数据对象怎么显示"。
75. ControlTemplate 用于什么? #
定义"控件自己长什么样"。
76. ObservableCollection 是否线程安全? #
不是线程安全的。跨线程修改常会报错,需要切回 UI 线程或做同步处理。
77. WPF 中跨线程更新 UI 怎么办? #
用 Dispatcher。
78. WPF 中最常见架构模式? #
MVVM。
第五部分:面试官最爱听的标准短句 #
1. 为什么用 MVVM? #
为了降低 View 和业务逻辑的耦合,提高可维护性、可测试性和复用性。
2. WPF 核心机制有哪些? #
数据绑定、依赖属性、路由事件、样式模板、命令机制。
3. 依赖属性有什么用? #
让属性能参与绑定、样式、动画和 WPF 属性系统。
4. ObservableCollection 为什么常用? #
因为集合增删变化时能自动通知 UI 刷新。
5. WPF 跨线程更新 UI 怎么做? #
通过 Dispatcher 切回 UI 线程。
6. 样式和模板区别? #
Style 改属性,Template 改结构和外观。
7. 绑定失败怎么看? #
先看 Output 窗口的绑定错误信息。
第六部分:15 个必背关键词 #
- MVVM
- DataContext
- Binding
- TwoWay
- INotifyPropertyChanged
- ObservableCollection
- ICommand
- DependencyProperty
- AttachedProperty
- RoutedEvent
- Style
- ControlTemplate
- DataTemplate
- Dispatcher
- Validation
第七部分:1 分钟自我介绍模板 #
我主要做 C# 和 WPF 桌面端开发,平时开发中比较常用 MVVM 架构,熟悉数据绑定、命令、依赖属性、路由事件、样式模板、资源字典和异步更新 UI 等内容。项目里也处理过列表性能优化、用户控件封装、输入校验、跨线程 UI 更新等问题。我比较关注代码解耦和可维护性,通常会通过 ViewModel、Command、Converter、ResourceDictionary 等方式优化项目结构。