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,把断点留给自己。”