C# wpf 运行时替换方法实现mvvm自动触发刷新

devtools/2024/9/20 3:56:54/ 标签: c#, wpf, .net, mvvm, hook

文章目录

  • 前言
  • 一、如何实现?
    • 1、反射获取属性
    • 2、定义替换方法
    • 3、交换属性的setter方法
  • 二、完整代码
    • 1、接口
    • 2、项目
  • 三、使用示例
    • 1、倒计时
      • (1)、继承ViewModelBase
      • (2)、定义属性
      • (3)、属性赋值
      • (4)、窗口关联ViewModel
      • (5)、xaml绑定
  • 总结


前言

我们知道,使用wpf的绑定功能,代码触发界面改变时需要在属性的setter触发PropertyChanged事件,以达到界面刷新的效果。上一章我们简化了触发流程,但是依然需要在每个属性的setter中调用方法。本章将再进一步简化,实现setter不需要调方法就可以自动触发界面刷新


一、如何实现?

1、反射获取属性

通过反射获取类的所有公有属性

var propertyInfos = type.GetProperties(bindingAttr: BindingFlags.Instance | BindingFlags.Public);

2、定义替换方法

定义的用于替换属性setter的方法,确保参数类型兼容。设置NoInlining确保不会被内联优化失去函数地址。再方法中触发RaisePropertyChangedEvent。

 [MethodImpl(MethodImplOptions.NoInlining)] void Setter0_obj(object value) { //此时Setter0_obj已经被替换成了属性的setter,调用会进入属性的setter中。Setter0_obj(value);RaisePropertyChangedEvent(_propertyInfos![0].Name); }

3、交换属性的setter方法

将定义的替换方法与属性的setter交换。MethodHooker.SwapMethod可以去搜索C#运行时替换函数的方法,本章的只是去掉了unsafe的实现。

var oldSetter = propertyInfos[i].GetSetMethod();
if (oldSetter != null && oldSetter.IsPublic)
//定义了set且set为公有时才交换。
{MethodInfo newSetter= type.BaseType.GetMethod("Setter0_obj", BindingFlags.Instance | BindingFlags.NonPublic)!;MethodHooker.SwapMethod(oldSetter, newSetter);
}

二、完整代码

1、接口

/// <summary>
/// ViewModelBase,继承此类可以简化属性的定义,不需要手动触发RaisePropertyChangedEvent。
/// 用法:继承此类,属性为公有,set为公有且非内联,设置属性就会自动触发mvvm的binding。
/// 实验性质,其他.net版本无效,在.net6.0是稳定的,。x64、x86,debug和release都可以使用。release需要给set设置[MethodImpl(MethodImplOptions.NoInlining)],否则无法实现函数交换。
/// 目前支持64个属性,单个属性(struct)最大128字节,需要更多可以自行调用GenSetters生成代码。
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged;public ViewModelBase();//依然提供此方法用于手动触发protected void RaisePropertyChangedEvent([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "");
}

2、项目

vs2022 .net 6.0项目。
https://download.csdn.net/download/u013113678/89241921
注:目前版本只能在.net6.0中正常使用,x64、x86、debug、release都没问题。其他.net版本大概率无效果或者异常。属于实验性质,请根据需要下载。


三、使用示例

1、倒计时

(1)、继承ViewModelBase

public class TimeTick : ViewModelBase

(2)、定义属性

set为公有,以及[MethodImpl(MethodImplOptions.NoInlining)]避免内联。支持非缺省set方法体,即可以在set中加入一些逻辑。

public class TimeTick : ViewModelBase
{public double Seconds { get; [MethodImpl(MethodImplOptions.NoInlining)] set; }=60;
}

(3)、属性赋值

public TimeTick()
{var time = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(1000) };time.Tick += (s, e) =>Seconds--;     time.Start();
}

(4)、窗口关联ViewModel

public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();DataContext = new TimeTick();}
}

(5)、xaml绑定

 <TextBlock Text="{Binding Seconds}" />

效果预览
在这里插入图片描述


总结

以上就是今天讲述的内容,替换函数原理很简单,但是具体实现还是比较麻烦的,尤其是需要适配不同的.net版本,本文目前只支持.net6.0。还有就是函数的参数,引用和值类型的区分,以及值类型的传值兼容,这些都是通过多次尝试才找个合理的方式。通过本文简化的ViewModelBase使用变的非常方便了,除了需要给set添加非内联属性,其他已经和普通属性没有区别。


http://www.ppmy.cn/devtools/24495.html

相关文章

TCP协议在物联网中实战

一、TCP协议介绍 网上对TCP协议介绍众多&#xff0c;本人按照自己的理解简单介绍一下。 TCP&#xff08;Transmission Control Protocol&#xff0c; 传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输控制层通信协议。 1.1 协议机制 1.1.1 三次握手 &…

$nextTick源码解析

this.$nextTick 是 Vue.js 内部使用的一个方法,用于在下一个 DOM 更新循环结束之后执行回调函数。 原理: nextTick 方法被调用后,会将回调函数存储在一个队列中Vue.js 会利用浏览器的异步队列机制,在 DOM 更新循环结束后执行这个队列中的所有回调函数。源码: /*** Defer…

LT1964ES5-5 低压差稳压器 200mA 贴片SOT-23-5 使用案例

LT1964ES5-5 微功耗 线性稳压器 LT1964ES5-5 是一款微功耗、低噪声、低dropout的负电压线性稳压器。它的功能是将输入电压转换为稳定的负输出电压&#xff0c;范围在-1.22V到-20V之间&#xff0c;最大输出电流为200mA。该器件特别适用于需要精密调节和低噪声电源的电路中&#…

对象存储服务的事件通知特性

AWS S3提供了事件通知的能力&#xff0c;即在客户应用访问对象存储时&#xff0c;对象存储服务可提供通知服务&#xff0c;支撑客户的特定应用场景&#xff0c;比如业务审计、计费等。 相关能力可参考官方文档。 相关API&#xff0c;如下&#xff1a; PutBucketNotificationGe…

Python AI库pandas读写数据库的应用操作——以sqlite3为例

Python AI库pandas读写数据库的应用操作——以sqlite3为例 本文默认读者具备以下技能&#xff1a; 熟悉python基础知识&#xff0c;vscode或其它编辑工具 已阅读Pandas基础操作文章,了解pandas常见操作 具备自主扩展学习能力 在数据分析和人工智能领域&#xff0c;pandas库和s…

【JS】找出两个数组中的相同元素与不同元素

一、找出相同元素 &#xff08;1&#xff09;方法一 const filterArr (arr1, arr2) > {let result [];for (let i 0; i < arr1.length; i) {for (let j 0; j < arr2.length; j) {if (arr1[i] arr2[j]) {result.push(arr1[i]);}}}return result; };&#xff08;…

基于迁移学习的语义句子分类器(微调一个预训练模型的案例,即迁移学习)

一、任务&#xff1a;基于迁移学习的语义句子分类器 项目介绍&#xff1a;本任务的目的是利用预训练好的模型来微调自己的微软研究释义语料库数据集&#xff0c;实现文本分类的任务&#xff0c;具体是判断两个句子的意思是否相同。在微调阶段&#xff0c;我们将整个模型的参数…

解析Redis Key Prefix配置之谜:双冒号“::”的由来与作用

前言 在使用Spring Boot集成Redis进行应用开发时&#xff0c;为了增强缓存键的可读性和管理性&#xff0c;我们常常会在配置文件中设定一个全局的key-prefix。如果你发现存储至Redis的键自动附加了“::”&#xff0c;本文将深入探讨这一现象背后的原因&#xff0c;解析Spring …

07_for循环返回值while循环

文章目录 1.循环返回值2.yield接收for返回值3.scala调用yield方法创建线程对象4.scala中的while循环5.scala中的流程控制 1.循环返回值 for循环返回值是Unit 原因是防止产生歧义&#xff1b; 2.yield接收for返回值 // 2.yield关键字打破循环&#xff0c;可以使for循环输出…

Linux抓包工具tcpdump

一、tcpdump抓包工具 1.命令格式解析 命令格式&#xff1a;tcpdump option proto dir type proto&#xff08;协议&#xff09; 1.tcp、udp、icmp 2.ip、ipv6 3.arp dir&#xff08;数据的方向 &#xff09; 1.src 192.168.7.130 只抓取源地址是7.130 2.…

Node.js 的 fs 模块分析及其应用

fs 模块&#xff0c;作为 Node.js 平台中的一个核心组件&#xff0c;主要负责处理文件系统相关的操作。该模块提供了一系列用于文件管理的功能&#xff0c;例如文件的读取、写入、更新以及删除等。 应用场景分析 fs 模块的应用范围广泛&#xff0c;下面是一些典型的使用实例&…

基于单片机的机械臂运行轨迹在线控制系统设计

摘要:基于PLC的机械臂运行轨迹控制系统通过PLC采集现场信号及输出信号的状态变化实现机械臂运行轨迹的控制,不能实现多自由度机械臂控制。设计基于单片机的机械臂运行轨迹在线控制系统,系统硬件由上位机PC在线控制、主控制板和机械臂舵机控制板构成,通过光电编码器位移传感…

maven冲突问题

在编写maven当中的依赖时&#xff0c;有时候会出现一些问题&#xff0c;这种问题为Maven的当中的依赖。 在导入依赖的时候&#xff1a;出现了两种依赖发生了版本冲突的问题&#xff1f; <?xml version"1.0" encoding"UTF-8"?> <project xmlns…

1394 笔记

RN节点工作模块&#xff0c;当接收到STOF包后开始计时&#xff0c;当达到节点的发送、接收、数据泵偏移时向应用层发送相应使能。并根据STOF判断CC的错误状态&#xff0c;只有以下条件全部满足时进入正常工作模式&#xff1a; 条件1&#xff0c;STOF消息的帧周期正确&#xff…

Rust的Vec<T>

Vec<T> 是 Rust 编程语言中用于表示动态大小的数组的数据结构。它提供了类似其他语言中动态数组&#xff08;如 C 的 std::vector 或 Python 的 list&#xff09;的功能&#xff0c;允许在运行时高效地添加、删除或修改元素&#xff0c;同时确保内存的有效管理。Vec<T…

GPS时钟服务器(北斗授时设备)助某数据中心项目部署

GPS时钟服务器&#xff08;北斗授时设备&#xff09;助某数据中心项目部署 GPS时钟服务器&#xff08;北斗授时设备&#xff09;助某数据中心项目部署 1.1 项目概述 本次某数据中心时间同步项目中&#xff0c;设计目标是为了满足该数据中心同一区域数万台设备的时间高精度同步要…

RustGUI学习(iced)之小部件(三):如何使用下拉列表pick_list?

前言 本专栏是学习Rust的GUI库iced的合集&#xff0c;将介绍iced涉及的各个小部件分别介绍&#xff0c;最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个&#xff0c;目前处于发展中&#xff08;即版本可能会改变&#xff09;&#xff0c;本专栏基于版本0.12.1. 概述…

typeScript 安装

1、安装typescript 安装npm i -g typescript 查看是否安装成功 tsc -v 2、使用ts // 浏览器不支持ts 需要编译成 es5 let str: string ts 在终端输入 tsc index.ts 会将其生成一个对应的index.js文件 在电脑上首次使用TS的时候&#xff0c;执行TS 的命令的时候报错 打开PowerS…

网际协议IP

一、概念导入 网际协议IP是TCP/IP体系中最重要的协议之一。与IP协议配套使用的还有三个协议&#xff1a; 地址解析协议ARP网际控制报文协议ICMP网际组管理协议IGMP 二、虚拟互联网络 &#xff08;1&#xff09;定义 现实世界中&#xff0c;不同网络的主机进行通信&#xf…

【matplot】【matlab】绘制简洁美观二维坐标系的一个例子

觉得下图不错美观大方&#xff0c;现仿制下图&#xff1a; import numpy as np import matplotlib.pyplot as pltdef sigmoid(x):return 1 / (1 np.exp(-x))def sigmoid_derivative(x):return sigmoid(x) * (1 - sigmoid(x))# 设置中文字体 plt.rcParams[font.family] [Tim…