2023-04-22
桌面应用
00
请注意,本文编写于 359 天前,最后修改于 342 天前,其中某些信息可能已经过时。

目录

引言
INotifyHolder接口
NoticeFlagAttribute特性
NotifyHolder的Binding管理器

引言

在实际开发场景中,当ViewModel内的一个属性是一个 ObservableCollection<T> 或者是一个多层级 class 的时候,有可能有的需求需要 ObservableCollection<T>内的元素的子属性或多层级 class 的子属性,甚至子属性的子属性,变化,需要通知到ViewModel,该怎么做呢?

例如我有一个设置功能模块,十几个模型,一两百个属性参数,模型之间是2~3层的嵌套关系,最后得到一个大模型表示Model,我想要在子属性的值变化的是通知到ViewModel,记录日志或其他操作。

现有的MVVM框架,例如 MVVMLightPrism , 我好像都没有找到这样的功能,如果有更好的方案或实现,烦请告之。

现在手动实现一个这样的辅助类。接下来看一下实现过程:

INotifyHolder接口

先定义 INotifyHolder 接口,用于通知 HolderViewModel ,有属性变化了。

csharp
namespace MvvmNoticeHolderLib { public interface INotifyHolder { void AfterPropertyChangedNotified(object sender, string info); } }

NoticeFlagAttribute特性

定义 NoticeFlagAttribute 特性,用于标记哪些属性是需要在变化时通知到 HolderViewModel 的。

csharp
namespace MvvmNoticeHolderLib { [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class NoticeFlagAttribute : Attribute { public string Name { get; set; } = string.Empty; public Type Type { get; set; } public NoticeFlagAttribute(string names, Type type) { Name = names; Type = type; } } }

NotifyHolder的Binding管理器

csharp
namespace MvvmNoticeHolderLib { public class NotifyHolderBindingManager { private static T BindSlaveProperty<T>(T source, object root) { try { if (source != null) { var type = source.GetType(); var properties = type.GetProperties(); var NoticeFlags = type.GetCustomAttributes<NoticeFlagAttribute>(); if (NoticeFlags != null && NoticeFlags.Count() > 0) { foreach (var noticeFlag in NoticeFlags) { PropertyInfo info = properties.SingleOrDefault(x => x.Name == noticeFlag.Name); if (info != null) { BindProperty(source, root, info); } } } } return source; } catch (Exception ex) { return source; } } public static T BindSelfProperty<T>(T root) { try { if (root != null) { var type = root.GetType(); var properties = type.GetProperties(); var NoticeFlags = type.GetCustomAttributes<NoticeFlagAttribute>(); if (NoticeFlags != null && NoticeFlags.Count() > 0) { foreach (var noticeFlag in NoticeFlags) { PropertyInfo info = properties.SingleOrDefault(x => x.Name == noticeFlag.Name); if (info != null) { BindSlaveProperty(root, root); var tmp = info.GetValue(root); if (root is INotifyPropertyChanged notify) { notify.PropertyChanged += (sender, e) => { if (NoticeFlags.Any(t => t.Name == e.PropertyName)) { var senderType = sender.GetType(); PropertyInfo senderProperty = senderType.GetProperty(e.PropertyName); BindProperty(sender, sender, senderProperty); } }; } } } } } return root; } catch (Exception) { return root; } } private static void BindProperty<T>(T source, object root, PropertyInfo info) { if (info.PropertyType.IsGenericType && info.PropertyType.GetGenericTypeDefinition() == typeof(ObservableCollection<>)) { var tmp = info.GetValue(source); if (tmp != null && tmp is INotifyCollectionChanged notifyCollectionChanged) { notifyCollectionChanged.CollectionChanged += (sender, e) => { if (e.NewItems != null && e.NewItems.Count > 0) { BindSlaveProperty(e.NewItems[0], root); if (e.NewItems[0] != null && e.NewItems[0] is INotifyPropertyChanged notify) { if (root is INotifyHolder notifyHolder) { notify.PropertyChanged += (s, e) => { notifyHolder.AfterPropertyChangedNotified(s, info.Name + "." + e.PropertyName + "发生了变化"); }; } } } }; var arr = (IEnumerable<object>)tmp; foreach (var item in arr) { if (item is INotifyPropertyChanged notify && root is INotifyHolder notifyHolder) { BindSlaveProperty(item, root); notify.PropertyChanged += (sender, e) => { notifyHolder.AfterPropertyChangedNotified(sender, info.Name + "." + e.PropertyName + "发生了变化"); }; } } } } else if (info.PropertyType.GetInterfaces().Contains(typeof(INotifyPropertyChanged))) { var tmp = info.GetValue(source); if (tmp != null && tmp is INotifyPropertyChanged notify) { BindSlaveProperty(tmp, root); if (root is INotifyHolder notifyHolder) { notify.PropertyChanged += (sender, e) => { notifyHolder.AfterPropertyChangedNotified(sender, info.Name + "." + e.PropertyName + "发生了变化"); }; } } } } } }

这个类就是实现这个功能的核心,其主要原理是,通过 NoticeFlagAttribute 特性,获取你要绑定的属性,然后 监控你要绑定的属性的 INotifyPropertyChangedPropertyChanged 事件或者是 INotifyCollectionChangedCollectionChanged事件,最后通知到 HolderViewModel 中,若子属性有多层级关系,可以多层级中每个层级使用 NoticeFlagAttribute 特性,标记你想要监控的属性,然后Binding管理器通过递归方式依次绑定好,就实现了多层级的监控通知到 HolderViewModel 中。

我已将Demo发布到github,Readme.md中有使用说明。

github仓库地址

https://github.com/PeterPandefu/MvvmNoticeHolder

本文作者:Peter.Pan

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!