[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="350" Width="525" 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="2"  Visibility="{Binding isWorking, Converter={StaticResource VisibilityConverter}}">
                <Border Background="Black"  Height="100" BorderBrush="Gold" Opacity="0.7" BorderThickness="1">
                    <StackPanel >
                        <TextBlock Text="请稍等" HorizontalAlignment="Center" Foreground="White"  Margin="0 10 0 0"></TextBlock>
                        <ProgressBar IsIndeterminate="True" Height="25" Margin="20"></ProgressBar>
                    </StackPanel>
                </Border>
            </Grid>
            <!-- 底层数据显示-->
            <ListView  Name="listview1">
                <ListView.View>
                    <GridView >
                        <GridViewColumn Header="学号" Width="80" DisplayMemberBinding="{Binding id}"/>
                        <GridViewColumn Header="姓名" Width="150" 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 = 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);
            }

        }

    }
}

主窗口核心代码:

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

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

//创建学生数组
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);这里是关键的解决办法,主窗体是主线程创建的,每个线程都有一个唯一的调度员,我们的工作就是命令调度员去做相应的工作,这里我们就相当于命令主窗体的线程调度员去增加数组成员,这样做线程是安全的,不会再有错误提示。

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

[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法

时间: 2024-12-06 21:36:54

[WPF]绑定到界面的数组不支持调度线程以外对其更改的办法的相关文章

WPF快速入门系列(4)——深入解析WPF绑定

一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信息来设置目标对象的属性.目标属性总是依赖属性.然而,源对象可以是任何内容,可以是一个WPF元素.或ADO.NET数据对象或自定义的数据对象等.下面详细介绍了WPF绑定中的相关知识点. 二.绑定元素对象 2.1 如何实现绑定元素对象 这里首先介绍绑定最简单的情况——绑定元素对象,即数据源是一个WPF元

Wix 安装部署教程(九) --用WPF做安装界面

经常安装PC端的应用,特别是重装系统之后,大致分为两类.一类像QQ,搜狗输入法这样的.分三步走的:第一个页面可以自定义安装路径和软件许可.第二个页面显示安装进度条,第三个页面推荐其他应用.先不管人家怎么实现的,我们先回顾一下. QQ:       再一个就是分六步或七步走的,如QQ影音:欢迎界面,用户许可,安装组件,安装目录,安装进度,安装完成,有七步的,一般会多一些软件推荐.当然还有其他的,比如是基于ClickOnce打包的,就一个界面,一个进度条.没有安装目录选择,这一般不是商业软件.先说第

WPF绑定之索引器值变化通知

背景 在某些应用中,需要在界面上绑定到索引器,并在值发生变化时实时更新. 解决方案 只要将包含索引器的类实现INotifyPropertyChanged接口,并在索引值更改时引发PropertyChanged事件,并将属性名称设置为Item[]即可.示例代码如下: public class NotifyDictionary : INotifyPropertyChanged { private readonly Dictionary<string, string> _dictionary = n

WPF绑定

WPF绑定使用的源属性必须是依赖项属性,这是因为依赖项属性具有内置的更改通知支持,元素绑定表达式使用了Xaml扩展标记,WPF绑定一个控件是使用Binding.ElementName,绑定非控件对象时使用Source,RelativeSource,DataContext属性(WPF特有,而非XAML),只能绑定对象的共有字段.下边是部分Binding 属性名,完整列表参考 :http://msdn.microsoft.com/zh-cn/library/vstudio/ms750413.aspx

C# WPF 一个设计界面

微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. C# WPF 一个设计界面 今天正月初三,大家在家呆着挺好,不要忘了自我充电. 武汉人民加油,今早又有噩耗,24号(8号)一路走好. 阅读导航 本文背景 代码实现 本文参考 源码 1. 本文背景 一个不错的界面设计 2. 代码实现 使用 .NET Framework 4.8 创建名为 "Dashboard1" 的WPF模板项目,添加3个Nuget库:MaterialDesignTh

WPF绑定的ListBox获取ListBoxItem及GoToState应用

现公司项目中需要制作一个扇形菜单,菜单项是用ListBox重写Style实现的,其数据是绑定的.菜单的每一项都有Normal,MouseOver和Selected三种状态,这三种状态当然可以通过鼠标移动和点击控制,但现在要通过代码来改变控件外观实现三种状态切换,该如何处理呢?   1.WPF绑定的ListBox获取ListBoxItem WPF中如果ListBox的ItemSource为绑定的,则ListBox.Items为绑定的数据源,而非ListBoxItem.如果直接通过如下代码会发现无法

apache支持中文域名绑定,apache支持中文域名绑定,教你怎样让apache支持中文域名绑定

摘要:apache支持中文域名绑定,apache支持中文域名绑定,教你怎样让apache支持中文域名绑定,根据本人实际经验,叫你如何让apache支持中文域名绑定,绝对管用的让apache支持中文域名绑定的方法.在百度搜索:中文域名在线转码然后在线转中文域名为punyco 我从事php开发网站已经有5年之久以前遇到一个同事问我怎么样才能将apache绑定中文域名,支付中文域名访问而我以在实际项目获得并总结的经验告诉他,以下是方法: 根据本人实际经验,叫你如何让apache支持中文域名绑定,绝对管

实战基础技能(07)--------WPF的登录界面的排版

一:截图 二:XAML代码 <Window x:Class="wpf练习.登录窗体" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="登录窗体" Height="200" Width="

WPF 绑定密码

原文:WPF 绑定密码 我们发现我们无法绑定密码框的密码,PasswordBox 的 Password 不能绑定. 我们想做 MVVM ,我们需要绑定密码,不能使用前台 xaml.cs 监听 密码改变得到密码的值,传到 ViewModel . 本文提供一个简单方法来绑定 WPF 的 PasswordBox 的 Password .这种方法不仅在 WPF 可以使用,在 UWP 也可以使用.关于 UWP 绑定密码,可以在我博客 win10 uwp 绑定密码 查看. 我在网上找的很多大神给出的可以解决