当前位置: 首页 > news >正文

记录一次附加属性失效全过程

1、我为了实现View到ViewModel的自动绑定,写了附加属性

local:ViewModelSelector.AutoWireViewModel="True"

没想到它不触发,我将dll的触发属性迁移到本地项目,触发了,但是报错MainViewModel找不到Text
2、我的xaml构造如下
MainView:

<Grid><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions><Button Width="200" Content="{Binding Name}" /><ContentControl Grid.Column="1" Content="{Binding View}" />
</Grid>

HomeView:

 td:ViewModelSelector.AutoWireViewModel="True"
<StackPanel><TextBlock Text="{Binding Text}" />
</StackPanel>

此时发现附加属性触发,但是viewModelType为null,
打断点尝试,发现ViewModel没有注册,
注册完成后运行,失败,原来在同一目录去查找了
继续修改,将View和ViewModel替换

var name = view.GetType().FullName.Replace("View", "ViewModel");
var viewModelType = Type.GetType(name);
if (viewModelType != null)
{var viewModel = TangdaoApplication.Provider.GetService(viewModelType);view.DataContext = viewModel;
}

继续运行,
界面已经能出现 “World” → HomeView.DataContext 确实是 HomeViewModel,绑定完全正确。
但 VS 依旧报 “MainViewModel 上找不到 Text” → 说明有一处绑定表达式并没有指向 HomeViewModel,而是指到了 MainViewModel;运行期只是碰巧在 HomeView 区域看见了正确的值
此时我使用PresentationTraceSources.TraceLevel=High炸出所有报错
我怀疑附加属性运行了两次
我使用

 view.ClearValue(FrameworkElement.DataContextProperty); 

切断继承链并延迟到Initialized去触发,成功了
成功,此时打包代码到dll,继续使用dll执行。
把完全相同的代码从 EXE 项目搬到 DLL 后,又出现 “MainViewModel 找不到 Text”
没触发???为此,我对dll的附加属性类所有行打上断点
继续运行
还是没触发???
我添加代码

 public HomeView(){InitializeComponent();bool flag = IT.Tangdao.Framework.Selectors.ViewModelSelector.GetAutoWireViewModel(this);System.Diagnostics.Debug.WriteLine($"[HomeView] AutoWireViewModel={flag}");}

是true,但是为什么不进断点
为什么附加属性不触发,我使用System.Diagnostics.Debugger.Break();强制进断点
可以进去,现在我怀疑是JIT问题

 // 1. 显式静态构造static ViewModelSelector(){// 2. 强制触摸字段,防止 JIT 死代码消除var dummy = AutoWireViewModelProperty;}

还是不起作用出现 “MainViewModel 找不到 Text”
上火了,我将能打断点的可疑地方全部上断点
直到

  private HomeView _view;public HomeView View{get => _view;set => SetProperty(ref _view, value);}

get方法卡死,导致VS2022卡出去,吆喝,来活了
我现在怀疑HomeView 实例并不是由 XAML 解析器创建的,

 View = TangdaoApplication.Provider.GetService<HomeView>();

我检查我的IOC容器,测压多次应该没问题,那么我对IOC容器解析没问题
但是以防万一,我将解析改为new,new HomeView()
还是卡死,此时
我改为从ViewModel先行

 private HomeViewModel _view;public HomeViewModel View{get => _view;set => SetProperty(ref _view, value);}View = TangdaoApplication.Provider.GetService<HomeViewModel>();

加上数据模板

  <DataTemplate DataType="{x:Type vm:HomeViewModel}"><view:HomeView /></DataTemplate>

此时不报错,界面成功出现数据

这是因为
代码 new 视图 + SetProperty 触发属性通知 → 设计器反复创建 → 递归死循环
这是 View-First 最典型的“设计时炸弹”,无论 new HomeView() 还是 IOC 解析 HomeView,只要视图由代码实例化并塞进属性通知链路,都会中招;
ViewModel-First 天然免疫,因为 XAML 解析器只在需要呈现时才创建视图,永远不会递归触发属性通知。

阶段 环境 现象 关键发现
初期调试 EXE 项目 附加属性不触发 代码在本地可调试
中期迁移 DLL 迁移 附加属性完全失效 静态构造函数可能被优化
架构调整 View-First 设计时递归卡死 new HomeView() 导致属性通知循环
最终方案 ViewModel-First 运行正常 数据模板 + IOC 容器解析
维度 View-First 模式 ViewModel-First 模式
实例化时机 代码中显式 new 或 IOC 解析 XAML 解析器按需创建
设计时支持 容易递归卡死 VS 天然免疫设计时问题
数据绑定 容易受继承污染 数据模板明确关联
内存管理 可能重复创建视图 按需创建,生命周期清晰
调试难度 高(属性通知循环) 低(清晰的创建链路)

最后总结一句话
“在 WPF 里,视图 new 得越少,世界越干净;
把 new 交给 XAML,把逻辑交给 VM,把断点留给自己。”

http://www.hskmm.com/?act=detail&tid=15359

相关文章:

  • Java 与物联网(IoT):边缘计算与智能终端应用
  • 为你的数据选择合适的分布:8个实用的概率分布应用场景和选择指南
  • AI 落地应用最新工具集
  • 台风呢
  • Day07-C:\Users\Lenovo\Desktop\note\code\JavaSE\Basic\src\com\David\method-Demo041比较难
  • Markdown基本与阿法
  • 对称二叉树
  • 一例电动车充电器防反接电路分析
  • isEmpty/isNotEmpty/isNotBlank/isBlank-isAnyEmpty/isNoneEmpty/isAnyBlank/isNoneBlank
  • NetCore+Web客户端实现gRPC实时推送
  • 个人项目-论文查重
  • 个人项目作业
  • 软工第二次作业--王腾
  • 牛客周赛 Round 110 E,F题解
  • 第5章:路由(Routing)与直连交换机(Direct Exchange)
  • 搜索百科(4):OpenSearch — 开源搜索的新选择
  • JAVA的计算方式
  • 安装 elasticsearch-9.1.4 - 集群 和 kibana-9.1.4
  • 反码 原码 补码
  • 线性结构常见应用之栈[基于郝斌课程]
  • 实测对比:权威榜单之公众号排版Top 5(含效果对比与适用建议)
  • go的泛型
  • 原码补码反码
  • lc1034-边界着色
  • 【汽车电子】汽车功能安全标准 ISO 26262
  • ISO 26262的不同安全等级:ASIL-D ASIL-C ASIL-B ASIL-A
  • C#学习1
  • 02020405 EF Core基础05-EF Core反向工程、EF Core和ADO.NET Core的联系、EF Core无法做到的事情
  • 02020406 EF Core基础06-EF Core生成的SQL
  • Gemini-2.5-Flash-Image-Preview 与 GPT-4o 图像生成能力技术差异解析​ - 教程