解决 WPF 绑定集合后数据变动界面却不更新的问题

独立观察员 2020 年 9 月 9 日

在 .NET Core 3.1 的 WPF 程序中打算用 ListBox 绑定显示一个集合(满足需求即可,无所谓什么类型的集合),以下是 Xaml 代码(瞟一眼就行,不是本文讨论重点):

<ListBox ItemsSource="{Binding SipRegistrations, Mode=OneWay}" SelectedValue="{Binding SelectedAccountBinding, Mode=OneWayToSource}"><ListBox.ItemTemplate><DataTemplate><TextBlock Text="{Binding SIPAccount.SIPUsername}"></TextBlock></DataTemplate></ListBox.ItemTemplate>
</ListBox>

ViewModel 中有一个目标集合,当前是一个 List。

属性变动通知有两种实现方式,一是使用 PropertyChanged.Fody,二是使用自定义绑定基类 BindableBase,如下图。

下面主要谈论数据变动(集合增加内容)后,前台的界面却没有更新的问题。具体来说就是,List.Add 之后,第一次有效果,但后面就没效果了,界面始终只显示一条数据。

原始(无效果):

SipRegistrations.RemoveAll(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername); // 移除重复项(如果有的话)
SipRegistrations.Add(binding); // 添加新项

猜想是因为 List 的引用并没有变化,所以被认为该属性没有改变,进而也就没有变动通知。

其实这种需要变动通知的情况,推荐使用的是 ObservableCollection:

但是本人之前使用 ObservableCollection 没有成功过,反而是使用 List 是可以的,所以还是先看看用 List 怎么解决吧。

变体一(调试时有几率有效果):

// 添加联系人到集合并处理界面绑定;
SipRegistrations.RemoveAll(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername);
List<SIPAccountBinding> tempList = SipRegistrations;    // 临时集合;
SipRegistrations = new List<SIPAccountBinding>();       // 目标集合先置为空;
tempList.Add(binding);                                  // 临时集合添加新项;
SipRegistrations = tempList;                            // 临时集合赋值给目标集合;

变体一通过临时变量做中转,强制让目标集合(的引用)发生改变,但结果是只在调试时以很小的概率成功过。

由于这部分代码是在异步逻辑里,所以有可能是在多线程环境,而 List 不是线程安全的,所以有了以下加锁版本的变体二。

变体二(无效果,应该是和变体一类似):

#region 成员/// <summary>
/// 加锁对象
/// </summary>
private object _lockObj = new object();#endregion// 加锁;
lock (_lockObj)
{// 添加联系人到集合并处理界面绑定;SipRegistrations.RemoveAll(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername);List<SIPAccountBinding> tempList = SipRegistrations;SipRegistrations = new List<SIPAccountBinding>();tempList.Add(binding);SipRegistrations = tempList;
}

加了锁还是不行(不过锁还是需要的),又想到,既然调试的时候有几率成功,那么是不是和代码运行速度有关呢?于是在目标集合置空和重新赋值之间加了个线程休眠,竟然真的可以,也就是以下的变体三。

变体三(有效果):

lock (_lockObj)
{// 添加联系人到集合并处理界面绑定;SipRegistrations.RemoveAll(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername);List tempList = SipRegistrations;SipRegistrations = new List();Thread.Sleep(500); // 关键代码;tempList.Add(binding);SipRegistrations = tempList;
}

好了,以上就是解决方法了。

接下来再尝试一下 ObservableCollection 吧:

#region 成员/// <summary>
/// 加锁对象
/// </summary>
private object _lockObj = new object();public ObservableCollection<SIPAccountBinding> SipRegistrations { get; set; } = new ObservableCollection<SIPAccountBinding>();#endregionlock (_lockObj)
{SipRegistrations.Remove(SipRegistrations.FirstOrDefault(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername));SipRegistrations.Add(binding);          // 情况一//SipRegistrations.Append (binding);     // 情况二
}Console.WriteLine($" 注册联系人 [{binding.RegisteredContact}] 为 [{sipAccount.SIPUsername}].");

这个有两种情况(都不能成功):

情况一,使用 Add 方法,结果是执行完 Add 方法后就返回了,后面的方法不再执行(不知道为什么),界面上是有几率能添加一条。

情况二,使用 Append 方法,执行完 Append 后倒是可以继续执行后面的代码,但是界面上一条也出现不了。

后记:本文主要是抛砖引玉,大家有什么更好的方法,或者能解释文中所描述现象的原理,请不吝赐教。

项目地址:https://gitee.com/DLGCY_GB28181/SimpleSIPServer


更新(20200909):

经过在 https://dotnet9.com/ 站长的技术讨论群的讨论,决定还是要使用 ObservableCollection。加上在网上搜到了文章《WPF ViewModel 中对 ObservableCollection 集合操作》,所以最终代码为:

#region 成员/// <summary>
/// 加锁对象
/// </summary>
private object _lockObj = new object();/// <summary>
/// 集合对象
/// </summary>
public ObservableCollection<SIPAccountBinding> SipRegistrations { get; set; } = new ObservableCollection<SIPAccountBinding>();#endregionlock (_lockObj)
{ThreadPool.QueueUserWorkItem(delegate{SynchronizationContext.SetSynchronizationContext(new System.Windows.Threading.DispatcherSynchronizationContext(Application.Current.Dispatcher));SynchronizationContext.Current?.Post(pl =>{SipRegistrations.Remove(SipRegistrations.FirstOrDefault(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername));SipRegistrations.Add(binding);}, null);});
}Console.WriteLine($" 注册联系人 [{binding.RegisteredContact}] 为 [{sipAccount.SIPUsername}].");

甚至还可以简化:

Application.Current.Dispatcher.Invoke(delegate
{SipRegistrations.Remove(SipRegistrations.FirstOrDefault(x => x.SIPAccount.SIPUsername == sipAccount.SIPUsername));SipRegistrations.Add(binding);
});

解决 WPF 绑定集合后数据变动界面却不更新的问题(使用 ObservableCollection)相关推荐

  1. 解决 WPF 绑定集合后数据变动界面却不更新的问题

    解决 WPF 绑定集合后数据变动界面却不更新的问题 独立观察员 2020 年 9 月 9 日 在 .NET Core 3.1 的 WPF 程序中打算用 ListBox 绑定显示一个集合(满足需求即可, ...

  2. 解决DataGridView绑定List后不能排序的问题

      阅读全文并下载例子 :http://www.sufeinet.com/forum.php?mod=viewthread&tid=190 以前不都是用table直接绑定DataGridVie ...

  3. 解决重启Linux服务器后数据消失问题

    目录 一.挂载 1.重新挂载 2.数据消失的原因 二.重启服务器 1.shutdown 2.其它关机重启指令 参考自 一.挂载 1.重新挂载 使用 reboot 命令重启服务器后,服务器内挂载的文件全 ...

  4. ComponentOne For WPF学习心得-实时数据

    ComponentOne For WPF学习心得-实时数据 写在前面 不断更新的数据 数据源绑定 写在前面 这是ComponentOne For WPF学习的第三篇文章,我对比着读了前两篇文章,感觉还 ...

  5. hp服务器通过ilo5安装系统,HPE ProLiant Gen10 通过iLO 5(v1.15) web界面多种方式更新服务器固件,包含升级系统恢复集方法...

    一.iLO web界面固件&操作系统软件界面简单介绍 1.固件 这个界面可以查看服务器安装的固件版本,可以查看以下类型的固件: u电源管理控制器Power Management Control ...

  6. [WPF 基础知识系列] —— 绑定中的数据校验Vaildation

    [WPF 基础知识系列] -- 绑定中的数据校验Vaildation 原文:[WPF 基础知识系列] -- 绑定中的数据校验Vaildation 前言: 只要是有表单存在,那么就有可能有对数据的校验需 ...

  7. 解决 IntelliJ IDEA 安装后界面消失,再次打开后界面不动

    解决 IntelliJ IDEA 安装后界面消失,再次打开后界面不动   为了能解析 JDK 17,笔者不得不下载当前最新版的 IntelliJ IDEA,结果在安装完 IntelliJ IDEA 之 ...

  8. JAVAWEB开发之SpringMVC详解(二)——高级开发、数据回显、参数绑定集合、图片上传、json交互、validation校验、异常处理、RESTful支持、拦截器

    知识回顾 springmvc框架 用户请求url到DispatcherServlet前端控制器,相当于中央调度器,降低系统各组件之间的耦合度. DispatcherServlet前端控制器通过Hand ...

  9. 使用python解决验证码登录并爬取登录后的个人界面:本篇以古诗文网站为例

    爬取总结记录:由于经验不足,个人尝试了很多方法,都失败了,查看了网站的上面别人的爬取经验,按照别人做的,但都没有成功. 几经波折最后终于解决了. 好了上代码: 其中有涉及到个人信息的,这里进行了模糊处 ...

最新文章

  1. Windows Phone开发(48):不可或缺的本地数据库
  2. VirtualProtect VirtualLock VirtualUnlock
  3. ibatis增删改、批量增删改以及查询
  4. ROS学习(三):ROS安装不能本地化问题
  5. 三个月计算机培训班,三个月复盘:学完两个设计软件,并开始学画画
  6. 牛客16596 计算系数(排列组合)
  7. 仿iphone触屏手机界面
  8. 压测学习总结(2)——Jmeter 基本知识入门
  9. html css回顾总结
  10. 两个应用程序之间的通信
  11. 【php基础入门】细说php的变量以及常量的知识点详解
  12. Eviews做ARIMA模型
  13. 循序渐进!疯狂java讲义第五版pdf蓝奏云下载
  14. CMake中cmake_minimum_required的使用
  15. 如何强制解锁或删除被占用的文件或文件夹 - ForceDelete绿色强制文件删除工具
  16. qq扫描用户文件传腾讯服务器,QQ被传窃取用户浏览器隐私,腾讯称为判断恶意登录而读取的数据...
  17. 微信小程序星级评分方法
  18. Java常用工具类(六):文件、资源、IO流工具类
  19. Thread.sleep(0)到底是个什么骚操作?
  20. java项目宕机出现原因,java服务宕机原因查询

热门文章

  1. 学习总结——Selenium元素定位
  2. Mac OS使用技巧之十五:快捷方便的Mini Dock
  3. linux 基准测试_如何对Linux系统进行基准测试:3个开源基准测试工具
  4. reddit_如何将多个子Reddit与多个Reddit合并
  5. 甘特图看起来很生硬?教你使用智能颜色装饰你的甘特图!(二)
  6. 如何成为有效学习的高手(许岑)——思维导图
  7. 基于netty的企业即时通讯系统的设计与实-离线消息处理
  8. 送给“苦逼”的IT人系列1:IT人的“钱”景以及收入的两道坎
  9. 读“苹果上周发布了Mac OS X Mountain Lion系统”新闻的感想
  10. .net framework3.0_