设计时向ViewModel添加用户自定义数据的最佳方案是什么?
设计时向ViewModel添加自定义数据的最佳实践
你当前通过查找指定名称ViewModel变量初始值做标记的方案存在几个明显缺陷:
- 依赖硬编码的字符串匹配变量名,没有编译期校验,重构时重命名变量、拼写错误都会直接导致判断逻辑失效
- 设计时标记用的元数据和运行业务变量混在同一个集合中,后续维护很难区分变量用途
- 判断前需要提前实例化
ViewModelUserControl、加载整个ViewModel实例,仅为了拿一个标记值属于不必要的性能开销 - 变量初始值属于ViewModel运行时状态的一部分,后续业务逻辑如果修改了这个值,会直接影响展示判断逻辑
方案1:强类型ViewModel优先用自定义特性附加元数据
这是.NET生态下给类型附加设计时元数据的标准实现,完全不侵入运行时业务逻辑,可维护性最高。
- 首先定义标记用的自定义特性:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ViewModelDisplayAttribute : Attribute { /// <summary> /// 面板展示标题 /// </summary> public string DisplayTitle { get; set; } /// <summary> /// 是否在无根子集列表中展示 /// </summary> public bool ShowInSubsetList { get; set; } = true; }
- 设计时直接在对应ViewModel类上打标记即可,不需要额外加占位变量:
[ViewModelDisplay(DisplayTitle = "用户自定义操作面板", ShowInSubsetList = true)] public class CustomBusinessViewModel : BaseViewModel { // 仅写正常业务逻辑,不需要额外定义vmUserTitle这类标记变量 }
- 运行时判断时,不需要提前实例化控件,直接通过反射读取ViewModel类型的特性即可:
// 先根据传入的name拿到对应ViewModel的Type Type vmType = GetViewModelTypeByName(name); var displayMeta = vmType.GetCustomAttribute<ViewModelDisplayAttribute>(); if (displayMeta is { ShowInSubsetList: true, DisplayTitle: not null and not "" }) { // 元数据符合要求,再实例化控件做后续逻辑 ViewModelUserControl vmc = new ViewModelUserControl(); vmc.ViewModelName = name; vmc.SetEcoSpace(ecoSpace); // 后续展示、业务处理逻辑 }
方案2:动态生成的无代码ViewModel用独立元数据注册表
如果你的ViewModel是通过设计器拖拽生成、没有对应强类型后端类的动态类型,无法直接加特性,就单独维护一份元数据映射表,不要把标记塞到ViewModel的变量集合里:
// 全局静态元数据注册表,设计时统一维护ViewModel和自定义标记的映射关系 public static class VmMetadataStore { private static readonly Dictionary<string, VmDisplayMeta> _metaMap = new() { ["CustomUserVm"] = new VmDisplayMeta { Title = "用户自定义面板", IsVisible = true }, ["DataStatisticVm"] = new VmDisplayMeta { Title = "数据统计面板", IsVisible = false } }; public static VmDisplayMeta GetDisplayMeta(string viewModelName) { return _metaMap.TryGetValue(viewModelName, out var meta) ? meta : null; } } public class VmDisplayMeta { public string Title { get; set; } public bool IsVisible { get; set; } }
运行时直接查询注册表判断即可,逻辑清晰,不会和业务变量耦合:
var meta = VmMetadataStore.GetDisplayMeta(name); if (meta is { IsVisible: true, Title: not null and not "" }) { // 再实例化控件做后续处理 ViewModelUserControl vmc = new ViewModelUserControl(); vmc.ViewModelName = name; vmc.SetEcoSpace(ecoSpace); // 后续业务逻辑 }
方案选型建议
- 只要是有对应强类型类的ViewModel,优先选自定义特性方案,编译期可校验,重构成本低,元数据和类型强绑定不会出错
- 动态生成的ViewModel选独立注册表方案,元数据统一维护,不要和运行时变量混放
- 你当前用的ViewModel变量存标记的方案仅适合临时原型验证,正式项目不要用,极易因为变量重命名、误改初始值出现隐蔽bug
内容的提问来源于stack exchange,提问作者Steve.Au
相关产品推荐
相关产品推荐

