博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法
阅读量:5300 次
发布时间:2019-06-14

本文共 10464 字,大约阅读时间需要 34 分钟。

[原]WPF编程经常遇到一个问题:

      某个数组己绑定到主界面某控件中,然后在后台程序中需要对数组增(减)数据,然后程序就会报错,

程序提示:该类型的CollectionView 不支持从调度程序线程以外的线程对其SourceCollection进行的更改。

如下图所示:

既然不能这样操作,就得想一个办法来解决,现在先把把出现错误的程序全部列出来,然后再来根据解决办法进行修改,

本测试程序先建一个学生类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ComponentModel;namespace WPF_test{    public class student : INotifyPropertyChanged     {        //定义数据更改事件通知和更改的方法        public event PropertyChangedEventHandler PropertyChanged;        public void up(string s)        {            if (this.PropertyChanged != null)            {                PropertyChanged(this, new PropertyChangedEventArgs(s));            }        }        //姓名        string _name;        public string name        {            get { return _name; }            set { _name = value; up("name"); }        }        //学号        string _id;        public string id        {            get { return _id; }            set { _id = value; up("name"); }        }        public student(string _id, string _name)        {            id = _id;            name = _name;        }        public student()        { }            }}
学生类代码View Code

主窗口xaml代码(将students数组绑定到主界面中):

主窗口xmal代码:View Code

等待动画类代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ComponentModel;namespace WPF_test{    class witeMe:INotifyPropertyChanged     {        public event PropertyChangedEventHandler PropertyChanged;        public void up(string s)        {            if (this.PropertyChanged != null)            {                PropertyChanged(this, new PropertyChangedEventArgs(s));            }        }        //工作提示状态        private bool _isWorking = false;        public bool isWorking        {            get { return _isWorking; }            set { _isWorking = value; up("isWorking"); }        }    }}
等待动画代码:View Code

主窗口核心代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Shapes;using System.Collections.ObjectModel;using System.Threading;using System.Threading.Tasks;namespace WPF_test{    ///     /// MainWindow.xaml 的交互逻辑    ///     public partial class MainWindow : Window    {        public MainWindow()        {            InitializeComponent();        }        ObservableCollection
students = new ObservableCollection
(); witeMe wm = new witeMe(); private void Window_Loaded(object sender, RoutedEventArgs e) { students.Add(new student("1号","张三")); students.Add(new student("2号", "李四")); listview1.ItemsSource = students; g1.DataContext = wm; } //出错测试 private void Button_Click(object sender, RoutedEventArgs e) { students.Clear(); Task tk = new Task(() => { Action
ac1 = (x, y) => students.Add(x); createStudents(ac1); }); tk.Start(); tk.ContinueWith((t) => MessageBox.Show("结束")); } //正确测试 private void Button_Click_1(object sender, RoutedEventArgs e) { students.Clear(); wm.isWorking = true; Task tk = new Task(() => { Action
ac2 = (x, y) => addData(x, y); createStudents(ac2); }); tk.Start(); } private void addData(student s, bool k) { ThreadPool.QueueUserWorkItem(delegate { this.Dispatcher.Invoke(new Action(() => { students.Add(s); if (k == true) wm.isWorking = false; // students.Insert(0, s);//这样也会很卡 //listview1.ScrollIntoView(s);//这个不行,大数据时真会卡死了 }), null); }); } //创建学生数组 private void createStudents(Action
_ac) { for (int i = 0; i < 100000; i++) { student s = new student(); s.id = (i + 1).ToString(); s.name = "小颗豆" + (i + 1).ToString(); if (i < 99999) _ac(s, false); else _ac(s, true); } } }}
主窗口核心代码:View Code

程序运行时:点击"错误测试"就会出现文章前边的错误图示,点击"正确测试"出现下图,一切正常:

正确测试时绑定的数组边修改,画面边显示,而且主窗口也没有卡死,鼠标也能拖动窗口,基本能达到目的了,下面分析一下代码是如何解决的:

//创建学生数组

private void createStudents(Action<student, bool> _ac)
{
for (int i = 0; i < 100000; i++)
{
student s = new student();
s.id = (i + 1).ToString();
s.name = "小颗豆" + (i + 1).ToString();
if (i < 99999)
_ac(s, false);
else
_ac(s, true);
}

}

在以上代码中每增加一个学生成员到数组中都是通过_ac(s,bool)委托进行的,

委托的定义是在异步线程中定义好的.即是ac2

Task tk = new Task(() =>

{

Action<student, bool> ac2 = (x, y) => addData(x, y);

createStudents(ac2);
});
tk.Start();

异步Task里,先定义了委托,每增加一个数组成员时委托addData方法在主界面调用者线程中由线程池去操作即可解决外线程不能更改数组的问题:

private void addData(student s, bool k)

{
ThreadPool.QueueUserWorkItem(delegate
{
this.Dispatcher.Invoke(new Action(() =>
{
students.Add(s);
if (k == true)
wm.isWorking = false;
// students.Insert(0, s);//这样会很卡,如果数据量小时则会显得很流畅

//listview1.ScrollIntoView(s);//这个不行,小数据无所谓,大数据时真会卡死界面了

}), null);
});
}

上边的代码中,

this.Dispatcher.Invoke(new Action(() =>

{
students.Add(s);
}),null);这里是关键的解决办法,主窗体是主线程创建的,每个线程都有一个唯一的调度员,我们的工作就是命令调度员去做相应的工作,这里我们就相当于命令主窗体的线程调度员去增加数组成员,这样做线程是安全的,不会再有错误提示。

以上是本人测试的例子,不足之处请批评指正,高手请飘过。

 

~~~~~~~~~ 给昨天写的内容还是再补充一下:~~~~~~~~~~~~~~~~~~

补充:

以上例程为了简化没有写成MVVM,如果写在MVVM的方式略有点不同,

代码如下:

//主界面代码
//主界面后台代码:namespace WPF_test{ /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { viewModel vm = new viewModel(); public MainWindow() { this.DataContext = vm; InitializeComponent(); } }}//NotifyUp.csnamespace WPF_test{ public class NotifyUp : INotifyPropertyChanged { //定义数据更改事件通知和更改的方法 public event PropertyChangedEventHandler PropertyChanged; public void up(string s) { if (this.PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(s)); } } }}//Model代码model.cs:namespace WPF_test{ public class model : NotifyUp { //学生管理类 studentMaster _studentMaster = new studentMaster(); public studentMaster StudentMaster { get { return _studentMaster; } set { _studentMaster = value; } } }}//student.cs学生类代码:namespace WPF_test{ public class student : NotifyUp { //姓名 string _name; public string name { get { return _name; } set { _name = value; up("name"); } } //学号 string _id; public string id { get { return _id; } set { _id = value; up("id"); } } public student(string _id, string _name) { id = _id; name = _name; } public student() { } }}//studentMaster.cs学生管理类代码:namespace WPF_test{ public class studentMaster : NotifyUp { ObservableCollection
_students = new ObservableCollection
(); public ObservableCollection
students { get { return _students; } set { _students = value; up("students"); } } //工作提示状态 private bool _isWorking = false; public bool isWorking { get { return _isWorking; } set { _isWorking = value; up("isWorking"); } } public studentMaster() { load(); } private void load() { students.Add(new student("1号", "张三")); students.Add(new student("2号", "李四")); } //异步线程增加学生成员 public void addStudent() { students.Clear(); isWorking = true; Task tk = new Task(() => { Action
ac2 = (x, y) => addData(x, y); createStudents(ac2); }); tk.Start(); } ///
//主窗口调度线程增加数组成员 /// ///
学生成员 ///
是否结束 private void addData(student s, bool isend) { ThreadPool.QueueUserWorkItem(delegate { System.Threading.SynchronizationContext.SetSynchronizationContext(new System.Windows.Threading.DispatcherSynchronizationContext(System.Windows.Application.Current.Dispatcher)); System.Threading.SynchronizationContext.Current.Send(pl => { students.Add(s);//students.Insert (0,s);//从数组前边加进成员,界面还是比较卡 if (isend) isWorking = false; }, null); }); } //创建学生数组 private void createStudents(Action
_ac) { for (int i = 0; i < 100000; i++) { student s = new student(); s.id = (i + 1).ToString(); s.name = "小颗豆" + (i + 1).ToString(); if (i < 99999) _ac(s, false); else _ac(s, true); } } }}
MVVM模式View Code

以上代码通过测试.

 

转载于:https://www.cnblogs.com/dooroo/p/3869096.html

你可能感兴趣的文章
DVD系统
查看>>
5.11题解 导弹拦截
查看>>
论三星输入法的好坏
查看>>
Linux 终端连接工具 XShell v6.0.01 企业便携版
查看>>
数据库体系
查看>>
JS写一个简单日历
查看>>
LCA的两种求法
查看>>
Python排序算法(四)——插入排序
查看>>
oo第三单元博客作业
查看>>
Jquery---定时器(实现页面内定时弹出广告,定时退出)
查看>>
day11-闭包函数和装饰器
查看>>
VSCode常用快捷键与流行插件
查看>>
从程序员到项目经理(14):项目经理必须懂一点“章法”【转载】
查看>>
JDBC批量插入优化addbatch
查看>>
复现一篇深度强化学习论文之前请先看了这篇文章!
查看>>
git 命令使用常见问题
查看>>
android加固系列—6.仿爱加密等第三方加固平台之动态加载dex防止apk被反编译
查看>>
decltype
查看>>
抽象类
查看>>
C++面向对象基础知识
查看>>