需求背景

需要显示 ViewModel 中的 Message/DpMessage,显示内容根据其某些属性来确定。代码结构抽象如下:

// Model
public class Message : INotifyPropertyChanged
{
public string MSG;
public string Stack;
} // ViewModel
public class MessageViewModel : INotifyPropertyChanged
{
public Message { get; set; }
public static readonly DpMessageProperty = DependencyProperty.Register(...)
}
<TextBox Text="{Binding Message, Converter={x:static ShowDetailMessageConverter}}"/>
<TextBox Text="{Binding DpMessage, Converter={x:static ShowDetailMessageConverter}}"/>

以上代码,注意,两个 Text 绑定的目标都是 MessageViewModel,绑定的 Path 分别是 Message、DpMessage。

当 Message 或者 DpMessage 变化(注意,变化指的是重新赋值,即,引用了新的 Message 实例),绑定的目标会收到通知,更新 UI。

问题来了

当 Message 或者 DpMessage 的属性变化了呢,Message 类是实现了 INotifyPropertyChanged 的,属性变化能触发自身的变化(MessageViewModel.INotifyPropertyChanged)通知吗?答案是,不能。即,Message .MSG 者 Message .Stack 变化了,View 不能得到更新通知!这不符合需求。

  • 当然,这个问题可以通过更改绑定对象避过,即,将绑定对象直接设置为 Message 或者 DpMessage,然后使用 MultiBinding,将需要的属性都绑定过去,也可以同样实现需求,但是,这种方式的缺点多:繁琐(如果涉及的属性数量非常大呢)、不直观(目标是 Message 整体,却绑定了其属性)等。

Message 属性(实现 INotifyPropertyChanged)的解决方法

在 Message .PropertyChanged 中监测属性变化,变化时主动调用 MessageViewModel.OnPropertyChanged。这是很简单的。

DpMessage 依赖属性的解决方法

不同于 INotifyPropertyChanged,依赖属性无法通过 OnPropertyChanged 函数触发属性变更通知,这个函数仅作为回调函数使用。因此,查看源码,看看.Net如何去触发的通知,找到函数如下:

/// <summary>
/// This is to enable some performance-motivated shortcuts in property
/// invalidation. When this is called, it means the caller knows the
/// value of the property is pointing to the same object instance as
/// before, but the meaning has changed because something within that
/// object has changed.
/// </summary>
/// <remarks>
/// Clients who are unaware of this will still behave correctly, if not
/// particularly performant, by assuming that we have a new instance.
/// Since invalidation operations are synchronous, we can set a bit
/// to maintain this knowledge through the invalidation operation.
/// This would be problematic in cross-thread operations, but the only
/// time DependencyObject can be used across thread in today's design
/// is when it is a Freezable object that has been Frozen. Frozen
/// means no more changes, which means no more invalidations.
///
/// This is being done as an internal method to enable the performance
/// bug #1114409. This is candidate for a public API but we can't
/// do that kind of work at the moment.
/// </remarks>
[FriendAccessAllowed] // Built into Base, also used by Framework.
internal void InvalidateSubProperty(DependencyProperty dp)
{
// when a sub property changes, send a Changed notification
// with old and new value being the same, and with
// IsASubPropertyChange set to true
NotifyPropertyChange(new DependencyPropertyChangedEventArgs(dp,
dp.GetMetadata(DependencyObjectType), GetValue(dp)));
}

注意函数说明部分:

when a sub property changes, send a Changed notification with old and new value being the same, and with IsASubPropertyChange set to true。

这完全符合我们的需求!!!这个函数本意是作为 Public API 的,但是由于性能的 bug #1114409,将其内部化了。那么,我可以通过反射去调用它:

{
this.InvokeInternal<DependencyObject>("NotifySubPropertyChange", new object[] { DpColorProperty });
} /// <summary>
/// 反射调用指定类型的 Internal 方法。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="caller"></param>
/// <param name="method"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public static object InvokeInternal<T>(this T caller, string method, object[] parameters)
{
MethodInfo methodInfo = typeof(T).GetMethod(method, BindingFlags.Instance | BindingFlags.NonPublic);
return methodInfo?.Invoke(caller, parameters);
}

好了,完美解决!

WPF 主动触发依赖属性的 PropertyChanged的更多相关文章

  1. WPF中的依赖属性

    1. WPF中的依赖属性 依赖属性是专门基于WPF创建的.在WPF库实现中,依赖属性使用普通的C#属性进行了包装,使用方法与普通的属性是相同的. 1.1 依赖属性提供的属性功能 资源 数据绑定 样式 ...

  2. WPF 精修篇 依赖属性

    原文:WPF 精修篇 依赖属性 依赖属性使用场景 1. 希望可在样式中设置属性. 2. 希望属性支持数据绑定. 3. 希望可使用动态资源引用设置属性. 4. 希望从元素树中的父元素自动继承属性值. 5 ...

  3. [No000012D]WPF(5/7)依赖属性

    介绍 WPF带来了很多传统 Windows 应用程序没有的新特性和选择.我们已经讨论了一些 WPF 的特性,是时候更进一步介绍其他特性了.当你读完这个系列之前的文章,我希望你已经或多或少地了解了 WP ...

  4. WPF教程:依赖属性

    一.什么是依赖属性 依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值.依赖属性可支持WPF中的样式设置.数据绑定.继承.动画及默认值. 将所有的属性都设置为依赖属性并不总是正确的解决 ...

  5. WPF usercontrol 自定义依赖属性

    1.依赖属性不同意一般属性,一般属性主要定义在对象中,而依赖属性是存在一个特殊的依赖属性表中.2.当我们触发改变值时,需要通过SetValue这种方式进行触发. UserControl1.xaml: ...

  6. (原创)2. WPF中的依赖属性之二

    1 依赖属性 1.1 依赖属性最终值的选用 WPF属性系统对依赖属性操作的基本步骤如下: 第一,确定Base Value,对同一个属性的赋值可能发生在很多地方.还用Button的宽度来进行举例,可能在 ...

  7. WPF学习笔记——依赖属性(Dependency Property)

    1.什么是依赖属性 依赖属性是一种可以自己没有值,并且通过Binding从数据源获得值(依赖在别人身上)的属性,拥有依赖属性的对象被称为"依赖对象". 依赖项属性通过调用 Regi ...

  8. 【转】【WPF】关于依赖属性的ValidateValueCallback,PropertyChangedCallback和CoerceValueCallback的执行顺序

    三个回调对应依赖属性的验证过程,改变过程和强制转换过程. class Dobj : DependencyObject { //依赖属性包装 public int MyProperty { get { ...

  9. WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)

    一. 摘要 首先圣殿骑士非常高兴这个系列能得到大家的关注和支持.这个系列从七月份開始到如今才第七篇,上一篇公布是在8月2日,掐指一算有二十多天没有继续更新了,最主要原因一来是想把它写好,二来是由于近期 ...

随机推荐

  1. Java 使用URL类通过url下载网络资源

    主要用到的类 地址类: URL http类: HttpURLConnection 输入流: InputStream 输出流: FileOutputStream 上代码 package com.demo ...

  2. unixbench性能测试跑分工具

    UnixBench是一个类unix系(Unix,BSD,Linux)统下的性能测试工具,一个开源工具,被广泛用与测试linux系统主机的性能 所谓跑分工具,不仅各项的测试有得分,最后跑完也会有一个综合 ...

  3. zabbix_server上的问题

    不要写成127.0.0.1,要不然一直包zabbix agent没有启动.

  4. 【Linux】rsync模板配置问题

    ------------------------------------------------------------------------------------------------- | ...

  5. 【Oracle】等待事件之 V$SESSION_WAIT

    (1)-V$SESSION_WAIT 这是一个寻找性能瓶颈的关键视图.它提供了任何情况下session在数据库中当前正在等待什么(如果session当前什么也没在做,则显示它最后的等待事件).当系统存 ...

  6. Kafka 探险 - 生产者源码分析: 核心组件

    这个 Kafka 的专题,我会从系统整体架构,设计到代码落地.和大家一起杠源码,学技巧,涨知识.希望大家持续关注一起见证成长! 我相信:技术的道路,十年如一日!十年磨一剑! 往期文章 Kafka 探险 ...

  7. 使用smartform打印表单

    昨天写了个smartform打印表单,在开发完成,在测试机测试OK,传到生产机,出现严重问题!无法打印,干脆就是无法调用打印图形界面,进入SMARTFORM事物,查看这个表单,发现,居然公司的LOGO ...

  8. ABAP 面试问题和答案

    What is an ABAP data dictionary?- ABAP 4 data dictionary describes the logical structures of the obj ...

  9. centos7搭建dolphinscheduler集群

    一.简述 Apache DolphinScheduler是一个分布式去中心化,易扩展的可视化DAG工作流任务调度系统.致力于解决数据处理流程中错综复杂的依赖关系,使调度系统在数据处理流程中开箱即用.有 ...

  10. httpd反向代理实践(一)

    div.example { background-color: rgba(229, 236, 243, 1); color: rgba(0, 0, 0, 1); padding: 0.5em; mar ...