9.2 WPF 核心概念速查
学习目标
- 根据需求快速定位 WPF 的对应机制。
- 记住通知、绑定、命令和线程各自的边界。
- 用状态表检查 TaskDesk 的交互条件。
概念与使用位置
| 概念 | 用来解决什么 | 后端经验对照 | 继续阅读 |
|---|---|---|---|
| XAML | 声明对象树、布局、属性 | 配置与页面声明,但也会创建对象 | XAML |
| Grid | 按行列分配空间 | 页面布局,不是数据表格 | 布局 |
| DataContext | 指定默认绑定源 | 页面当前模型,沿元素树继承 | 绑定源 |
| Binding | 把属性与 UI 连接 | 状态持续同步,不是一次请求输出 | DataContext |
| 属性通知 | 通知某个值已经变化 | 类似事件发布 | 属性与集合 |
| 集合通知 | 通知行增删、替换、移动 | 不等同于数据库变更跟踪 | 属性与集合 |
| ICommand | 执行动作及判断可执行性 | 业务入口加交互条件 | 命令 |
| 依赖属性 | 支持绑定、样式、动画等属性系统 | 不等于依赖注入 | 属性与事件 |
| 资源与 Style | 复用值和外观规则 | 公共配置与主题 | 资源样式 |
| DataTemplate | 决定数据项如何显示 | 数据的呈现模板 | 模板 |
| ControlTemplate | 改变控件内部视觉结构 | 控件实现外观 | 模板 |
| Dispatcher | 调度 UI 线程操作 | UI 对象具有线程归属 | 异步线程 |
| Host 与 DI | 组织服务、配置与生命周期 | 熟悉的容器,作用域要显式设计 | 依赖注入 |
绑定写法
下面 XAML 是独立属性用法合集。复制到窗口时,需要已经设置 MainViewModel 为 DataContext,并使用其 NewTitle、Todos、SelectedTodo、AddCommand 和 StatusMessage。实际布局仍需容器,见本地 examples/TaskDesk/TaskDesk.Client/MainWindow.xaml。
<TextBox Text="{Binding NewTitle, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="新增" Command="{Binding AddCommand}" />
<DataGrid ItemsSource="{Binding Todos}" SelectedItem="{Binding SelectedTodo, Mode=TwoWay}"
IsReadOnly="True" CanUserAddRows="False" />
<TextBlock Text="{Binding StatusMessage}" TextWrapping="Wrap" />
Mode=TwoWay 表示目标变化也写回源,UpdateSourceTrigger 表示何时写回,这是两个不同维度。TextBox.Text 的默认行为不等于所有属性的默认行为。Binding 是路径解析:路径写错通常产生运行时调试信息,不一定阻止构建。
通知与源生成
| 要做的事 | 正确机制 | 注意 |
|---|---|---|
| 改一个文本属性 | INotifyPropertyChanged 或 ObservableObject | UI 绑定公开属性 |
| 增加或删除行 | ObservableCollection<T> | 修改发生在 UI 线程 |
| 修改普通行属性 | 行对象自己发送通知 | 集合不会替行属性通知 |
| 修改不可变 DTO | 替换集合中的 DTO | 同时处理所选对象引用 |
| 更新按钮状态 | CanExecuteChanged | 属性变化不会自动推导所有命令 |
| Toolkit 字段生成 | ObservableProperty + partial 类 | newTitle 生成 NewTitle |
| Toolkit 异步命令 | RelayCommand + 返回 Task | RefreshAsync 生成 RefreshCommand |
TaskDesk 命令条件
| 命令 | 可执行条件 | 何时通知 |
|---|---|---|
RefreshCommand | 未忙碌 | IsBusy 变化 |
AddCommand | 未忙碌,标题去空格后非空且不超过 120 | NewTitle 或 IsBusy 变化 |
CompleteCommand | 未忙碌,所选任务未完成 | SelectedTodo 或 IsBusy 变化 |
DeleteCommand | 未忙碌,存在选中项 | SelectedTodo 或 IsBusy 变化 |
CancelCommand | 忙碌 | IsBusy 变化 |
门禁应同时出现在可执行性与操作入口,恢复放进 finally。命令 token 经服务传入 HTTP;UI 不调用同步等待。取消与超时后先确认写操作是否已在服务端发生。
生命周期与 HTTP
WPF 主窗口通常比 HTTP 请求活得更久。桌面 DI 没有自动请求作用域,弹窗或编辑会话需要自行创建、结束 scope。App 使用容器创建窗口时,应移除 StartupUri;配置要复制到输出目录。
HTTP 地址与 JSON 字段属于契约;400 是输入不合法,404 是资源不存在,401/403 涉及身份和授权。HttpClient 的 I/O 直接 await。任务取消不等于事务回滚;客户端隐藏按钮也不等于服务端授权。
Prism 9 速查
下表对应 Prism.DryIoc 9.0.537 和本地 examples/PrismStudy。不要用旧版命名空间替换表中的 API。
| 需求 | API 与命名空间 | 本站说明 |
|---|---|---|
| 组合应用 | PrismApplication:Prism.DryIoc | 启动与容器 |
| 注册与解析 | IContainerRegistry、IContainerProvider:Prism.Ioc | 页面、服务、对话框分别注册 |
| 自动配对 ViewModel | ViewModelLocator:Prism.Mvvm | 按约定命名或显式映射 |
| 属性通知 | BindableBase.SetProperty:Prism.Mvvm | MVVM 与命令 |
| 同步/异步命令 | DelegateCommand、AsyncDelegateCommand:Prism.Commands | 条件变化后通知可执行性 |
| 页面导航 | IRegionManager、INavigationAware:Prism.Navigation.Regions | Region 导航 |
| 参数与导航结果 | NavigationParameters、NavigationResult:Prism.Navigation | 结果看 Success、Exception、Cancelled |
| 未保存确认 | IConfirmNavigationRequest:Prism.Navigation.Regions | 所有分支完成一次回调 |
| 对话框 | IDialogService、IDialogAware:Prism.Dialogs | 对话框,关闭用 DialogCloseListener |
| 页面间通知 | IEventAggregator、PubSubEvent<T>:Prism.Events | 事件聚合,规划线程与退订 |
| 功能模块 | IModule、IModuleCatalog:Prism.Modularity | 模块化,初始化不等于 Region 已就绪 |
完整操作和验收见 PrismStudy 实战。容器组织对象,Region 组织界面,对话框承载需要返回结果的交互,事件表达已发生的变化;需要等待结果的业务操作仍通过服务方法完成。
常见问题
把“集合更新”和“行属性更新”混在一起,是通知问题的常见来源。把“异步”和“后台线程”视为同义词,则容易造成 UI 线程错误。先用表格定位责任,再到 FAQ 按步骤验证。
练习与验收
给定“完成请求失败、按钮一直禁用”,从本页列出三个检查点,并在代码中定位它们。验收答案应包含 finally 恢复忙碌、IsBusy 的命令通知、异常是否被正确处理。再说明“已完成任务的完成按钮禁用”由哪个属性变化驱动。