首页 > 其他分享 >MVVM --- 实现多层级通知

MVVM --- 实现多层级通知

时间:2023-08-05 09:11:13浏览次数:41  
标签:info 层级 sender MVVM --- var null root Name

引言

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

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

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

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

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 特性,获取你要绑定的属性,然后 监控你要绑定的属性的 INotifyPropertyChangedPropertyChanged 事件或者是 INotifyCollectionChangedCollectionChanged事件,最后通知到 HolderViewModel 中,若子属性有多层级关系,可以多层级中每个层级使用 NoticeFlagAttribute 特性,标记你想要监控的属性,然后Binding管理器通过递归方式依次绑定好,就实现了多层级的监控通知到 HolderViewModel 中。

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

github仓库地址

https://github.com/PeterPandefu/MvvmNoticeHolder

标签:info,层级,sender,MVVM,---,var,null,root,Name
From: https://www.cnblogs.com/pandefu/p/17536262.html

相关文章

  • 【雕爷学编程】Arduino动手做(182)---DRV8833双路电机驱动模块
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来—小小的进步或是搞......
  • ERP-SQL查询
    --查询垫布的颜色/尺码--工作单物料需求-物料需求明细selectm.OrderNo,m.MatrClass,m.MatrCode,m.Color,m.Sizxfrommrjmdtlmwherem.MatrClass='DB'intodsResult1;--查询出料单中已出库且无单价的物料信息--出料单(每一笔出库数据)/物料名称/收货方/出库金额=0--......
  • 正点原子ARM裸机开发003----汇编LED驱动实验1-原理分析
    一、汇编LED原理分析为什么要学习Coretex-A汇编?需要用汇编初始化一些SOC外设使用汇编初始化DDR,IMX6U不需要设置sp指针,一般指向DDR,设置好C语言运行环境ALPHA开发板LED灯硬件原理分析:STM32 IO初始化流程:使能GPIO时钟设置IO复用,将其复用为GPIO配置GPIO的电气属性使用G......
  • vue--day54--todolist 中的MyItem 和App 消息发布实现通信
    1.App.vue<template><divid="root"><divclass="todo-container"><divclass="todo-wrap"><!--@addTodo事件名addTodo回调名--><MyHeader@addTodo="addTodo"/><!--父亲给儿子传数据父亲通过数据绑定......
  • QT(4)信号、SLOT和QMap - Addressbook例子2
    在之前的MeeGo开发者(五):QT(3)对象和继承小例子基础上,我们增加三个button,参考http://doc.qt.nokia.com/latest/tutorials-addressbook-part2.html、http://doc.qt.nokia.com/latest/tutorials-addressbook-part3.html和http://doc.qt.nokia.com/latest/tutorials-addressbook-part......
  • automate-dv 基于dbt 的data vault 2.0 落地工具
    automate-dv基于dbt的datavault2.0落地工具,包装了不少方便的宏,可以方便我们呢进行datavault2.0数据仓库建模理论的实际落地功能企业级保障支持datavault的hub,links,satelintes以及一些扩展(方便使用)元数据驱动的代码生成dbt包配置支持多平台支持,支持多种数据库平......
  • android mvvm实例解析
    MVVM架构,将整个应用分为三层,View层,VM层,Model层。其中View层单向引用VM层,VM层单向引用Model层。如上图。单向引用,而非双向引用,这是MVVM与MVP最大的区别。View层,只是单向引用VM层,VM层不需要引用View层,但是却可以更新View层。这是通过VM层的观察者模式实现的,在这里使用架构组件Liv......
  • 亚德客-DPS系列电子式数显压力传感器-说明书详解(2/2)
    本文讲解的是说明书的——2、基本设定模式  具体步骤:1、在当前显示画面长按蓝色的SET键 2、进入基础设计模式然后按下图设置即可......
  • mybatis-plus中的@Select注解里面写sql语句的in
    @Select("<script>"+"select\n"+"email\n"+"fromsys_user\n"+"whereidin\n"+"<foreachitem='item'index='index'collection='ids'open='(&......
  • 洛谷 P7911 [CSP-J 2021] 网络连接 题解
    写在前面一道普及级别的题目。CSP-J全国统一命题2021年第三题。本题解来自于一位真正的大佬。传送门https://www.luogu.com.cn/blog/xyf007/solution-p7911。题面信息来源于洛谷。请访问https://www.luogu.com.cn/problem/P7911。声明:本题解非商业用途,一切侵权行为请联系作......