[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法
[原]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()
{ } }
}
学生类代码
主窗口xaml代码(将students数组绑定到主界面中):
<Window x:Class="WPF_test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:zz="clr-namespace:WPF_test"
Title="MainWindow" Height="" Width="" Loaded="Window_Loaded" >
<Window.Resources >
<zz:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<DockPanel >
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Center" Margin="0 10 ">
<Button Content="出错测试" Margin="20 0" Click="Button_Click" />
<Button Content="正确测试" Margin="20 0" Click="Button_Click_1" />
</StackPanel>
<Grid>
<!-- 顶层等待动画-->
<Grid Name="g1" Panel.ZIndex="" Visibility="{Binding isWorking, Converter={StaticResource VisibilityConverter}}">
<Border Background="Black" Height="" BorderBrush="Gold" Opacity="0.7" BorderThickness="">
<StackPanel >
<TextBlock Text="请稍等" HorizontalAlignment="Center" Foreground="White" Margin="0 10 0 0"></TextBlock>
<ProgressBar IsIndeterminate="True" Height="" Margin=""></ProgressBar>
</StackPanel>
</Border>
</Grid>
<!-- 底层数据显示-->
<ListView Name="listview1">
<ListView.View>
<GridView >
<GridViewColumn Header="学号" Width="" DisplayMemberBinding="{Binding id}"/>
<GridViewColumn Header="姓名" Width="" DisplayMemberBinding="{Binding name}"/>
</GridView>
</ListView.View>
</ListView>
</Grid> </DockPanel>
</Window>
主窗口xmal代码:
等待动画类代码:
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"); }
}
}
}
等待动画代码:
主窗口核心代码:
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
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
} ObservableCollection<student> students = new ObservableCollection<student>();
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<student, bool> 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<student, bool> 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<student, bool> _ac)
{
for (int i = ; i < ; i++)
{
student s = new student();
s.id = (i + ).ToString();
s.name = "小颗豆" + (i + ).ToString();
if (i < )
_ac(s, false);
else
_ac(s, true);
} } }
}
主窗口核心代码:
程序运行时:点击"错误测试"就会出现文章前边的错误图示,点击"正确测试"出现下图,一切正常:
正确测试时绑定的数组边修改,画面边显示,而且主窗口也没有卡死,鼠标也能拖动窗口,基本能达到目的了,下面分析一下代码是如何解决的:
//创建学生数组
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的方式略有点不同,
代码如下:
//主界面代码
<Window x:Class="WPF_test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:zz="clr-namespace:WPF_test"
Title="MainWindow" Height="" Width="" >
<Window.Resources >
<zz:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<DockPanel >
<Button DockPanel.Dock="Bottom" Content="MVVM增加数组成员测试" Margin="20 0" Command="{Binding AddStudents}" />
<Grid>
<!-- 顶层等待动画-->
<Grid Name="g1" Panel.ZIndex="" Visibility="{Binding myModel.StudentMaster.isWorking, Converter={StaticResource VisibilityConverter}}">
<Border Background="Black" Height="" BorderBrush="Gold" Opacity="0.7" BorderThickness="">
<StackPanel >
<TextBlock Text="请稍等" HorizontalAlignment="Center" Foreground="White" Margin="0 10 0 0"></TextBlock>
<ProgressBar IsIndeterminate="True" Height="" Margin=""></ProgressBar>
</StackPanel>
</Border>
</Grid>
<!-- 底层数据显示-->
<ListView Name="listview1" ItemsSource="{Binding myModel.StudentMaster.students}">
<ListView.View>
<GridView >
<GridViewColumn Header="学号" Width="" DisplayMemberBinding="{Binding id}"/>
<GridViewColumn Header="姓名" Width="" DisplayMemberBinding="{Binding name}"/>
</GridView>
</ListView.View>
</ListView>
</Grid> </DockPanel>
</Window> //主界面后台代码:
namespace WPF_test
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
viewModel vm = new viewModel();
public MainWindow()
{
this.DataContext = vm;
InitializeComponent();
}
}
} //NotifyUp.cs
namespace 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<student> _students = new ObservableCollection<student>();
public ObservableCollection<student> 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<student, bool> ac2 = (x, y) => addData(x, y);
createStudents(ac2);
});
tk.Start();
} /// <summary>
//主窗口调度线程增加数组成员
/// </summary>
/// <param name="s">学生成员</param>
/// <param name="isend">是否结束</param>
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<student, bool> _ac)
{
for (int i = ; i < ; i++)
{
student s = new student();
s.id = (i + ).ToString();
s.name = "小颗豆" + (i + ).ToString();
if (i < )
_ac(s, false);
else
_ac(s, true);
}
} }
}
MVVM模式
以上代码通过测试.
[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法的更多相关文章
- WPF中,多key值绑定问题,一个key绑定一个界面上的对象
问题说明: 当用到dictionary<key,value>来储存数据的时候,有时候需要在界面上绑定一个key来显示value,这时候有两种思路: 一种是写一个自定义的扩展类,类似Bind ...
- WPF快速入门系列(4)——深入解析WPF绑定
一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...
- 【转】【WPF】WPF绑定用法
一.简介 为了后面行文顺利,在进入正文之前,我们首先对本文所涉及到的绑定知识进行简单地介绍.该节包含绑定的基本组成以及构建方式. WPF中的绑定完成了绑定源和绑定目标的联动.一个绑定常常由四部分组成: ...
- Wix 安装部署教程(九) --用WPF做安装界面
经常安装PC端的应用,特别是重装系统之后,大致分为两类.一类像QQ,搜狗输入法这样的.分三步走的:第一个页面可以自定义安装路径和软件许可.第二个页面显示安装进度条,第三个页面推荐其他应用.先不管人家怎 ...
- WPF绑定之索引器值变化通知
背景 在某些应用中,需要在界面上绑定到索引器,并在值发生变化时实时更新. 解决方案 只要将包含索引器的类实现INotifyPropertyChanged接口,并在索引值更改时引发PropertyCha ...
- 【转】WPF绑定模式
源地址:http://www.cnblogs.com/zjz008/archive/2010/05/26/1744802.html http://blog.csdn.net/haylhf/articl ...
- wpf绑定全局静态变量(mvvm)
原文 wpf绑定全局静态变量(mvvm) 在实际的开发中,有一些集合或者属性可能是全局的,比如当你做一个oa的时候,可能需要展示所有的人员,这时这个所有的人员列表显然可以作为全局参数,比如这里有一个全 ...
- WPF绑定功能常用属性介绍
1.Mode 绑定中数据流的方向(enum BindingMode) 目标属性指的是控件的属性 (1)TwoWay 更改源属性或目标属性时,会自动更新另一方.适用于可编辑窗体 例:TextBox (2 ...
- WPF绑定各种数据源之xml数据源
一.WPF绑定各种数据源索引 WPF 绑定各种数据源之Datatable WPF绑定各种数据源之object数据源 WPF绑定各种数据源之xml数据源 WPF绑定各种数据源之元素控件属性 Bindin ...
随机推荐
- 我们用CloudStack做什么
原文地址:http://www.sdfengxi.com/?p=376 我想很多同学会有类似的疑问,就是我配置好了CloudStack或者OpenStack之类的环境之后能够提供什么服务或者应用呢?下 ...
- SCC模板
vector<int> G[maxn]; int pre[maxn], low[maxn], c[maxn]; int n, m; stack<int> s; int dfst ...
- php2go - Go 实现 PHP 常用内置函数
[转]http://www.syyong.com/Go/php2go-Use-Golang-to-implement-PHP-s-common-built-in-functions.html 使用Go ...
- 【BZOJ2832&&3874】宅男小C [模拟退火][贪心]
宅男小C Time Limit: 10 Sec Memory Limit: 256 MB[Submit][Status][Discuss] Description 众所周知,小C是个宅男,所以他的每 ...
- bzoj1855: [Scoi2010]股票交易 单调队列优化dp ||HDU 3401
这道题就是典型的单调队列优化dp了 很明显状态转移的方式有三种 1.前一天不买不卖: dp[i][j]=max(dp[i-1][j],dp[i][j]) 2.前i-W-1天买进一些股: dp[i][j ...
- django执行sql
http://docs.30c.org/djangobook2/chapter10/ def first_names(self, last_name): cursor = connection.cur ...
- 用python写爬虫笔记(一)
https://bitbucket.org/wswp/code http://example.webscraping.com http://www.w3schools.com selenium.g ...
- IPC网络高清摄像机基础知识4(Sensor信号输出YUV、RGB、RAW DATA、JPEG 4种方式区别) 【转】
转自:http://blog.csdn.net/times_poem/article/details/51682785 [-] 一 概念介绍 二 两个疑问 三 RAW和JPEG的区别 1 概念说明 3 ...
- shell脚本(傻瓜式处理文件到指定分类)
前言 每一到两周,我大概会新增十多个甚至更多的资料文件,都是些最近遇到的一些问题的总结或者相关技术文档,但是资料都是在公司电脑上,拷贝到自己电脑上后,又得一个个去找一个这个应该放到哪个分类,个人感觉很 ...
- Restful接口设计
URL设计规范:/模块/资源/{标示}/集合1/... eg: /user/{uid}/friends ->好友列表 例子:秒杀系统API设计 1.请求参数绑定:@PathVariable(&q ...