今天用WPF的View绑定了ViewModel的一个属性类,结果在属性类的子属性修改时,没有通知到UI.

如有要显示一个学生信息,采用WPF MVVM的模式,则前端代码

   <StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:"/>
<TextBlock Text="{Binding Student.Name}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:"/>
<TextBlock Text="{Binding Student.Age}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="学级:"/>
<TextBlock Text="{Binding Student.Grade}"/>
</StackPanel>
</StackPanel> 

Student实体类,外部引入 PropertyChanged.Fody 第三方库作为自动属性通知

   public class Student: INotifyPropertyChanged
{
public string Name { get; set; }
public int Age { get; set; }
public int Grade { get; set; } public event PropertyChangedEventHandler PropertyChanged;
}

ViewMode

    public ICommand TestCmd => new RelayCommand(() =>
{
Student = new Student();
Random random = new Random();
Student.Name = "张三";
Student.Age = random.Next(5,9);
Student.Grade = 1;
});

测试一下

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

上面的代码是没问题的,也是最常见的思路,因为直接绑定的类的子属性,在子属性修改时,通知UI。

不过我的需求则是:当学生的年龄与学级的差大于6时,则需要在原来的年龄上有超龄提醒。

比如:

那么修改代码,前端

   <StackPanel Grid.Row="0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="姓名:"/>
<TextBlock Text="{Binding Student.Name }"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="年龄:"/>
<TextBlock Text="{Binding Student,Converter={StaticResource StudentAgeConverter}}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="学级:"/>
<TextBlock Text="{Binding Student.Grade }"/>
</StackPanel>
<Button Command="{Binding TestCmd}" Content="测试"/>
</StackPanel>

新增加的转换器代码

  public class StudentAgeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
if (value is Student student)
{
if (student.Age - student.Grade > 6)
{
return $"超龄,实际年龄为{student.Age}";
}
return student.Age;
}
return null;
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

开始调试

纳尼???,怎么年龄一直是默认值呢!!!所有的类我都实现了 INotifyPropertyChanged 接口,为什么这个年龄没有被通知到?

为什么姓名和年级就可以正常显示呢?他们看起来并没有什么不一样,不就一个直接绑的对象,一个绑的对象的具体属性吗,为什

么绑的对像的这个没有被通知到呢?

仔细想想,前端我Age其实绑定的是Student,之所以显示Age,是因为转换器把Student的Age提了出来。也就是说,当Student的

子属性被修改时,并不触发PropertyChanged。

和之前代码的区别就是一个直接绑定的子属性,子属性修改自然会通知UI,而这个则是绑定的类,类的子属性修改,并不会通知UI。

那么解决的思路就有两个了:

方法1.前端的Age 改为采用多重绑定来直接绑定 Student 的Age 和Student 的Grade,然后转换器也改为实现IMultiValueConverter,然后

转换器的传参用Object数组来接收...,再考虑到VS对Xmal的多重绑定的支持并不太好,我头就更大了,虽然最后还是写出来了,这

里就不放出来了。

方法2.简单点想,在修改子属性的时候,手动通知Student不就可以了嘛

说干就干,修改下后端命令代码

       public ICommand TestCmd => new RelayCommand(() =>
{
Student = new Student();
Random random = new Random();
Student.Name = "张三";
Student.Age = random.Next(5,9);
Student.Grade = 1;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Student")); });

调试一下

OK,可以了。不过想了想,还是让Student的Age属性自己触发可能更好点,这样就不用担心如果其他地方修改了Age,导致

没有手动触发而显示错误的异常了。

Student的实体类修改为

   public class Student : INotifyPropertyChanged
{
public object obj { get; set; }
public string Name { get; set; }
private int age; public int Age
{
get { return age; }
set
{
age = value;
PropertyChanged?.Invoke(obj, new PropertyChangedEventArgs("Student"));
}
} public int Grade { get; set; } public event PropertyChangedEventHandler PropertyChanged;
}

生成实体时,当前的VIewmodel赋值到Student的obj上

        public ICommand TestCmd => new RelayCommand(() =>
{
Student = new Student();
Student.obj = this;
Random random = new Random();
Student.Name = "张三";
Student.Age = random.Next(5, 9);
Student.Grade = 1;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Student")); });

测试同样可以正常运行。

后来我又想,如果把Viewmodel的Student直接注册为依赖属性,是不是就可以了。

结果是不行,而且,因为是注册成了依赖属性,依赖属性是不支持手动通知的,导致不管怎么修改都

不能正确显示正确结果。

后来,面向了搜索引擎了一下,发现这篇文章  https://blog.csdn.net/Liwuqingxin/article/details/81141856 ,

按照他提供的方法,修改代码如下:

添加针对依赖属性的扩展方法

   public static class Dependencyextension
{
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);
}
}

ViewModel的Student改为依赖属性

      public Student Student
{
get { return (Student)GetValue(StudentProperty); }
set { SetValue(StudentProperty, value); }
} public static readonly DependencyProperty StudentProperty =
DependencyProperty.Register("Student", typeof(Student), typeof(MainWindowViewModel), null);

TestCmd改为

       public ICommand TestCmd => new RelayCommand(() =>
{
Student = new Student();
Student.obj = this;
Random random = new Random();
Student.Name = "张三";
Student.Age = random.Next(5, 9);
Student.Grade = 1;
this.InvokeInternal<DependencyObject>("NotifySubPropertyChange", new object[] { StudentProperty }); });

调试测试符合预期。

WPF PropertyChanged实现子属性通知的更多相关文章

  1. 属性通知之INotifyPropertyChanged

    为什么后台绑定的值改变了前台不发生变化了? 针对这个初学者很容易弄错的问题,这里介绍一下INotifyPropertyChanged的用法 INotifyPropertyChanged:用于绑定属性更 ...

  2. 【WPF学习笔记】之WPF基础:依赖关系属性和通知

    这些天来,对象似乎已经忙得晕头转向了.每个人都希望它们做这做那.Windows® Presentation Foundation (WPF) 应用程序中的典型对象会接到各种各样不同的请求:有要求绑定到 ...

  3. WPF 主动触发依赖属性的 PropertyChanged

    需求背景 需要显示 ViewModel 中的 Message/DpMessage,显示内容根据其某些属性来确定.代码结构抽象如下: // Model public class Message : IN ...

  4. WPF的依赖项属性

    WPF的依赖项属性 属性与事件是.NET抽象模型的核心部分.WPF使用了更高级的依赖项属性(Dependency Property)功能来替换原来.NET的属性,实现了更高效率的保存机制,还添加了附加 ...

  5. WPF 精修篇 数据绑定 更新通知

    原文:WPF 精修篇 数据绑定 更新通知 开始更新一点有意思的了 首先 数据绑定  其中之一 Element 绑定 看例子 <Window x:Class="WpfApplicatio ...

  6. WPF 动态生成对象属性 (dynamic)

    原文:WPF 动态生成对象属性 (dynamic) 项目中列行的数据 都需要动态生成 所以考虑到对象绑定  可需要一个动态生成属性的意思 缺点 加载速度会慢 很明显的慢 解决办法 尽量减轻动态属性的量 ...

  7. WPF中的依赖属性

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

  8. WPF 绑定到静态属性(4.5)

    1. 声明静态事件 /// <summary> /// 静态属性通知 /// </summary> public static event EventHandler<Pr ...

  9. WPF笔记(1.3 属性元素)——Hello,WPF!

    原文:WPF笔记(1.3 属性元素)--Hello,WPF! 这一节中“属性元素”的概念可以用匪夷所思形容.1.WPF用标签元素实现对象建模,有两种:Control和Container,都用来装载内容 ...

随机推荐

  1. expression动态构成

    http://blog.csdn.net/tastelife/article/details/7340205 http://blog.csdn.net/tastelife/article/detail ...

  2. 并发编程之:ForkJoin

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 在JDK1.7中引入了一种新的Fork/Join线程池,它可以将一个大的任务拆分成多个小的任务并行执行并汇总执行结果. Fork/Join采用的是分而 ...

  3. Django学习day09随堂笔记

    每日测验 """ 今日考题: 1.choices参数的应用场景有哪些,如何获取该字段的值 2.django是什么模型的框架,简述MTV与MVC模型 3.多对多表关系有几种 ...

  4. cordova 打包 守护进程无法启动

    方案 1 : 添加环境变量 _JAVA_OPTIONS = -Xmx512M 2: 在新建的 系统变量里 变量名   _JAVA_OPTIONS 变量值   -Djava.net.preferIPv4 ...

  5. 鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据 | 百篇博客分析OpenHarmony源码 | v33.02

    百篇博客系列篇.本篇为: v33.xx 鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁 ...

  6. PolarDB PostgreSQL 架构原理解读

    背景 PolarDB PostgreSQL(以下简称PolarDB)是一款阿里云自主研发的企业级数据库产品,采用计算存储分离架构,兼容PostgreSQL与Oracle.PolarDB 的存储与计算能 ...

  7. IdentityServer4[1]:开篇

    1.开篇 首先明确一点,文章只是学习过程的笔记,参考目前网络上的博客,主要便于自己加深理解,同时也督促自己持续学习,没有其他目的.感谢网上资源的提供者. IdentityServer是为ASP.NET ...

  8. IL合集

    由于之前写的表达式树合集,未编写任何注释且是以图片的形式展现给大家,在这里向各位看官道歉了,接下来为大家奉上新鲜出炉的香喷喷的IL合集,后面会持续更新,各位看官点关注不迷路,之前答应的手写IOC以及多 ...

  9. 题解 CF241E Flights

    题目传送门 题目大意 给出一个 \(n\) 个点 \(m\) 条边的 \(\texttt{DAG}\) ,给每条边设定边权为 \(1\) 或者 \(2\) ,使得 \(1\to n\) 的每条路径长度 ...

  10. SpringBoot入门06-Thymeleaf显示作用域对象种的对象

    作用域对象request,session, servletContext中的数据在Thymeleaf中的显示都是相同的 作用域对象中的 List和Set的集合在html中的显示是相同的 作用域对象中的 ...