7.28 从“图形状态切换“理解 Parse 与状态驱动模式
从图形状态切换理解 Parse 与状态驱动模式面向初学者 | 无代码纯思路目录先看一个场景什么是 Parse为什么图形能自动变色一个数字装下很多个状态为什么不用一个状态属性而是散落的 9 个三个转换器各管一摊从头到尾走一遍总结先看一个场景想象你在操作一个检测软件画布上有很多矩形图形。当发生以下情况时图形外观会变化鼠标点击图形 → 边框变虚线检测结果 OK → 背景变绿色检测结果 NG → 背景变红色正在扫描 → 背景变橙色既没选中也没状态 → 默认实线边框问题来了程序怎么知道一个图形当前是什么状态然后自动切换颜色什么是 ParseParse 读懂一份数据翻译成另一种格式。生活类比原始数据翻译后看体温计水银柱“38.5 度”看红绿灯亮红灯“停”读成绩单“张三 数学 85”{名字:“张三”, 科目:“数学”, 成绩:85}程序里的 Parse 完全一样输入一堆碎片信息 → 输出一个有意义的整体为什么图形能自动变色第一步ViewModel 告诉外界我有什么属性每个图形对象对外暴露一组属性全是是/否这个图形被选中了吗 → 是 / 否 这个图形被鼠标悬停了吗 → 是 / 否 这个图形正在被扫描吗 → 是 / 否 这个图形扫描完了吗 → 是 / 否 这个图形正在重建吗 → 是 / 否 这个图形重建完了吗 → 是 / 否 这个图形正在检测吗 → 是 / 否 这个图形检测结果 OK → 是 / 否 这个图形检测结果 NG → 是 / 否总共 9 个问题每个答案不是是就是否。第二步WPF 的多路绑定把这些答案打包送出WPF 有一个机制叫MultiBinding多路绑定它能把多个属性同时、按顺序送给一个处理函数MultiBinding 说我把 9 个属性的值打包成一个数组你按顺序拿—— 第一个位置 → 是否选中 第二个位置 → 是否悬停 第三个位置 → 是否扫描 ... 第九个位置 → 是否 NG第三步Parse 函数把是/否转换成状态对象收到的就是一堆 true / false比如[true, false, true, false, false, false, false, false, false]Parse 的工作读懂这些 true/false 分别表示什么把它们合并成一个复合状态。[true, false, true, ...] ↓ 这个图形被选中了 正在扫描中第四步转换器根据状态决定颜色Parse 输出的状态交给不同的转换器Parse 输出的状态 │ ┌────────────────┼────────────────┐ 边框转换器 背景转换器 描边转换器 决定线型 决定背景色 决定描边色每个转换器做的事情就是查表如果状态里有NG → 给红色 如果状态里有正在检测 → 给青色 如果状态里有选中 → 给虚线 如果什么都没有 → 给实线默认色有优先级NG OK 检测中 扫描中 悬停 选中 默认。一个数字装下很多个状态你会问一个图形同时选中又正在扫描又结果 NG怎么存用位标记技巧把每个状态分配一个二进制位选中 → 第 0 位 悬停 → 第 1 位 扫描中 → 第 2 位 检测中 → 第 5 位 结果 OK → 第 6 位 结果 NG → 第 7 位 ...一个 int 有 32 位足够装下所有状态。一个图形选中 扫描中 NG → 第 0 位置 1、第 2 位置 1、第 7 位置 1 → 最终存成一个整数查询时很简单状态里有没有 NG→ 看看第 7 位是不是 1。特殊处理OK 和 NG 互斥一个检测结果不可能同时 OK 又 NG。所以加一条规则如果传入OK → 先加上 OK 标记再擦掉 NG 标记 如果传入NG → 先加上 NG 标记再擦掉 OK 标记后写入的会覆盖前一个。基础语法课堂如果你是初学者下面这些写法可能会让你困惑。我们用人话解释一下。位运算|、|、、~这组符号用来操控位标记状态。加标记|OR或我有一个状态 选中第 0 位是 1 我想再加正在扫描 选中 | 正在扫描 结果 选中 正在扫描两个 1|的作用是只要任意一边的位是 1结果就是 1。所以适合往上加标记。|是|的简写状态 状态 | 正在扫描 ← 完整写法 状态 | 正在扫描 ← 简写效果一样就像x x 5可以简写成x 5。查标记AND与想知道状态里有没有NG 状态 NG → 如果结果不为 0说明有 NG的作用是只有两边的位都是 1结果才是 1。状态 选中 | NG 第 0 位和第 10 位是 1 状态 NG 非零 → 有 NG 状态 OK 0 → 没有 OK擦除标记~配合NOT取反想擦掉状态里的OK标记 状态 状态 (~OK)~把 OK 的二进制位全部翻转原来 OK 对应位是 1翻转后变成 0原来别的位是 0翻转后全是 1。然后用去与一下OK 对应的位会变成 0被擦掉其他位保持不变。OK 0001000000 假设 OK 占第 6 位 ~OK 1110111111 除了第 6 位其他全是 1 状态 ~OK → OK 对应的位被清 0其他位原样保留三元表达式条件 ? 是 : 否这是if-else的迷你版if (条件) { 取值A; } else { 取值B; } 等价于条件 ? 取值A : 取值B通常用来做防御防止传入的值是 null 导致崩溃传入值可能是 null → 如果是 null用 false 代替 传入值可能是 abc → 不是 bool也用 false 代替 传入值 true → 正常使用is bool 变量名模式匹配这个东西同时干了三件事某个值 is bool s第几步干什么举例①判断这个值是不是 bool 类型null 不是 / “abc” 不是 / true 是②如果是把值存到变量 s 里s true③如果否s 不存在整个表达式为 false—变量名没有含义叫 s、flag、x 都行用完就扔。极简写法某个值 is true不需要起变量名直接判断是不是 true。null 和 false 都会被视为不是 true。WPF MultiBinding 怎么传参数WPF 会把 XAML 里绑定的属性按顺序打包成数组XAML 写的顺序 第 1 个绑定 → 数组[0] 第 2 个绑定 → 数组[1] 第 3 个绑定 → 数组[2]下标就是绑定的序号。转换器收到的就是一个object[]里面每个位置对应一个绑定的属性值。注意东西类型是object而不是bool。因为 WPF 的绑定系统在传值时不检查类型什么值都能塞进去null、字符串、数字……都可能。所以转换器里必须做类型判断。一句话总结这堆语法符号作用一句话加标记加标记简写查标记“看看某一位是不是 1”~取反“除了这一位其他全变” ~擦标记“把某一位清零”? :三元“迷你 if-else”is bool s模式匹配“判断 取值 防御”数组[下标]取值“第几个绑定的属性”为什么不用一个状态属性而是散落的 9 个理想的设计是ViewModel 直接暴露一个状态属性而不是 9 个散落的 true/false。// 理想 图形.当前状态 选中 | 扫描中 | 结果OK // 实际情况 图形.是否选中 true 图形.是否扫描中 true 图形.是否结果OK true ...为什么没按理想来因为这些 true/false 属性不只是为 UI 颜色服务的。它们还要被业务逻辑直接读取如果选中了 → 启用删除按钮 如果正在扫描 → 禁用编辑这些业务判断直接读是否选中比查位标记要直观。所以属性先以散落的 bool 形式存在后来要给 UI 加自动变色功能时用 MultiBinding 打了个补丁。这是真实项目中常见的历史选择——不是最优方案但改动成本高于收益。按长度分组的潜在问题Parse 函数里有一段按传了多少个属性做分组判断的逻辑。这种写法有一个隐患如果一个图形只绑了是否选中这一个属性长度1那么是否选中的状态会被静默跳过永远解析不到。现在没出 bug纯粹因为所有用到这个转换器的图形都恰好绑了至少两个属性但并没有任何机制保证这一点。三个转换器各管一摊转换器管什么怎么判断边框转换器实线/虚线/无线只看选中和悬停背景转换器填充色按 NG OK 检测中 扫描 悬停 优先级描边转换器边框颜色同背景逻辑但用不透明颜色同一个状态值三个转换器分别读自己关心的部分互不干扰。从头到尾走一遍1. 用户点击一个图形 → 图形.是否选中 true 2. WPF 检测到属性变化 → 把 9 个属性值打包[true, false, false, ...] 3. Parse 处理 → 读第一个位置true → 打上选中标记 → 读其他位置都是 false → 不加标记 → 输出状态 选中 4. 边框转换器拿到选中 → 查表选中 → 虚线 5. 背景转换器拿到选中 → 按优先级查表不是 NG不是 OK不是检测...是选中 → 透明色 6. 描边转换器拿到选中 → 选中 → 返回默认描边色 7. 画布刷新 → 图形边框变虚线用户看到视觉反馈整个过程无需手写任何如果 xx 就把颜色设为 yy的代码全部由转换器自动处理。总结Parse 读懂原始数据翻译成结构化信息。像体温计把水银柱翻译成38.5 度。MultiBinding 把多个属性同时打包送给一个处理函数。像快递员一次性取走 9 个包裹。位标记 一个整数装下多个是/否。每个状态占一个二进制位加一起就是一个整数。转换器 查表决策。输入状态 → 按优先级查表 → 输出对应的颜色/线型。散落的 9 个 bool 是历史选择。不是最优但改动成本高于收益。整个模式的本质散落的 bool 值数据 ↓ Parse 结构化状态语义 ↓ Converter 视觉表现颜色/线型数据 → 语义 → 表现三层分离各不越界。