MVVM --- 实现多层级通知
引言
在实际开发场景中,当ViewModel内的一个属性是一个 ObservableCollection<T> 或者是一个多层级 class 的时候,有可能有的需求需要 ObservableCollection<T>内的元素的子属性或多层级 class 的子属性,甚至子属性的子属性,变化,需要通知到ViewModel,该怎么做呢?
例如我有一个设置功能模块,十几个模型,一两百个属性参数,模型之间是2~3层的嵌套关系,最后得到一个大模型表示Model,我想要在子属性的值变化的是通知到ViewModel,记录日志或其他操作。
现有的MVVM框架,例如 MVVMLight ,Prism , 我好像都没有找到这样的功能,如果有更好的方案或实现,烦请告之。
现在手动实现一个这样的辅助类。接下来看一下实现过程:
INotifyHolder接口
先定义 INotifyHolder 接口,用于通知 HolderViewModel ,有属性变化了。
namespace MvvmNoticeHolderLib
{
public interface INotifyHolder
{
void AfterPropertyChangedNotified(object sender, string info);
}
}
NoticeFlagAttribute特性
定义 NoticeFlagAttribute 特性,用于标记哪些属性是需要在变化时通知到 HolderViewModel 的。
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管理器
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 特性,获取你要绑定的属性,然后 监控你要绑定的属性的 INotifyPropertyChanged 的PropertyChanged 事件或者是 INotifyCollectionChanged 的 CollectionChanged事件,最后通知到 HolderViewModel 中,若子属性有多层级关系,可以多层级中每个层级使用 NoticeFlagAttribute 特性,标记你想要监控的属性,然后Binding管理器通过递归方式依次绑定好,就实现了多层级的监控通知到 HolderViewModel 中。
我已将Demo发布到github,Readme.md中有使用说明。
github仓库地址
MVVM --- 实现多层级通知的更多相关文章
- CommunityToolkit.Mvvm8.1 消息通知(4)
本系列文章导航 https://www.cnblogs.com/aierong/p/17300066.html https://github.com/aierong/WpfDemo (自我Demo地址 ...
- 说不尽的MVVM(3) – 从通知属性说起
上篇我们体验了一个从事件处理程序到MVVM程序的转变,在最后也留下了一个问题:RaisePropertyChanged的原理是什么?今天我们来一探究竟. 通过上节做的小例子我们知道,仅仅修改ViewM ...
- android MVC && MVP && MVVM分析和对比
相关:http://www.cnblogs.com/wytiger/p/5305087.html 出处http://blog.csdn.net/self_study,对技术感兴趣的同鞋加群544645 ...
- iOS开发项目之MVC与MVVM
MVC MVC,Model-View-Controller,我们从这个古老而经典的设计模式入手.采用 MVC 这个架构的最大的优点在于其概念简单,易于理解,几乎任何一个程序员都会有所了解,几乎每一所计 ...
- ReactiveCocoa 和 MVVM 入门 (转)
翻译自ReactiveCocoa and MVVM, an Introduction. 文中引用的 Gist 可能无法显示.为了和谐社会, 请科学上网. MVC 任何一个正经开发过一阵子软件的人都熟悉 ...
- 【长篇高能】ReactiveCocoa 和 MVVM 入门
翻译自ReactiveCocoa and MVVM, an Introduction. 文中引用的 Gist 可能无法显示.为了和谐社会, 请科学上网. MVC 任何一个正经开发过一阵子软件的人都熟悉 ...
- 【转】伟大的RAC和MVVM入门(一)
原文:http://www.sprynthesis.com/2014/12/06/reactivecocoa-mvvm-introduction/ 翻译自ReactiveCocoa and MVV ...
- javascript基础修炼(9)——MVVM中双向数据绑定的基本原理
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 概述 1.1 MVVM模型 MVVM模型是前端单页面应用中非常重要的模型之一,也是Single Page Appl ...
- 基于vue实现一个简单的MVVM框架(源码分析)
不知不觉接触前端的时间已经过去半年了,越来越发觉对知识的学习不应该只停留在会用的层面,这在我学jQuery的一段时间后便有这样的体会. 虽然jQuery只是一个JS的代码库,只要会一些JS的基本操作学 ...
- 使用MVVM设计模式构建WPF应用程序
使用MVVM设计模式构建WPF应用程序 本文是翻译大牛Josh Smith的文章,WPF Apps With The Model-View-ViewModel Design Pattern,译者水平有 ...
随机推荐
- C/C++ 常用的四种查找算法
在计算机科学中,搜索算法是一种用于在数据集合中查找特定元素的算法.C语言作为一种强大的编程语言,提供了多种搜索算法的实现方式.本文将介绍C语言中的四种常见搜索算法其中包括(线性查找,二分法查找,树结构 ...
- 【算法】【回溯】两道经典排列问题OJ详解【力扣46 力扣47】【超详细的回溯算法教程】让我们牢牢把握回溯的精髓
[算法][回溯]两道经典排列问题OJ详解[力扣46 力扣47][超详细的回溯算法教程]让我们牢牢把握回溯的精髓 作者: @小小Programmer 这是我的主页:@小小Programmer 在食用这篇 ...
- 联想T30瘦客户机安装DoraOS体验
硬件配置:J4125 .8G RAM. 128G ROM 联想T30台式电脑,它是一台迷你计算机,尺寸小巧玲珑,重量适中,方便携带.它的性能十分强大,能够运行各种应用程序,包括网页浏览器.视频播放器等 ...
- 零基础入门学习Java课堂笔记 ——day05
面向对象(上) 面向过程:我打算列个计划表一步一步来 面向对象:我喜欢先分析分类,把复杂的问题简单化 1.什么是面向对象!!? 面向对象的本质就是:以类的方式组织代码,以对象的方式组织数据 封装 继承 ...
- 【OpenVINO™】在 MacOS 上使用 OpenVINO™ C# API 部署 Yolov5
在 MacOS 上使用 OpenVINO C# API 部署 Yolov5 项目介绍 YOLOv5 是革命性的 "单阶段"对象检测模型的第五次迭代,旨在实时提供高速.高精度的结果, ...
- 【架构师视角系列】QConfig配置中心系列之架构设计(一)
目录 声明 配置中心系列文章 一.架构 基础模型 架构图 架构分层 运行规则 模块划分 Admin模块 Client模块 Server模块 二.总结 三.最后 声明 原创文章,转载请标注.https: ...
- ABC 310
E \(dp[i][j]\) 表示前 \(i\) 个里有多少个后缀答案为 \(j\). \(if (c[i] == '0') \{\) \(dp[i][0] = 1;\) \(dp[i][1] = d ...
- ES6学习 第三章 字符串的扩展
前言 本章主要是内容为是 ES6 对字符串的改造和增强. 本章记录字符串的扩展常用重点部分,不常用知识稍作记录. 本章原文链接: 字符串的扩展 模板字符串(template string) ES6字符 ...
- NC16670 [NOIP2006]能量项链
题目链接 题目 题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗 ...
- Linux 下配置Oracle开机自动启动
一./etc/oratab说明 直接使用cat 查看这个文件: gg1:/home/oracle> cat /etc/oratab # # This file is used by ORACLE ...