富客户端 wpf, Winform 多线程更新UI控件

前言  

在富客户端的app中,如果在主线程中运行一些长时间的任务,那么应用程序的UI就不能正常相应。因为主线程要负责消息循环,相应鼠标等事件还有展现UI。

因此我们可以开启一个线程来格外处理需要长时间的任务,但在富客户端中只有主线程才能更新UI的控件。

解决方法

简单的来说,我们需要从其他的线程来更新UI线程的控件,需要将这个操作转交给UI线程(线程marshal)。

方法1:

在底层的操作中,可以有以下的方法:

  • WPF中,在element的Dispatcher类中调用BeginInvoke或者Invoke方法
  • Metro中,在Dispatcher类中调用RunAsync或者Invoke方法
  • Winform中,在控件中直接调用BeginInvoke或者Invoke方法

以上所有的方法的参数都是一个Delegate,用此Delegate来代表需要处理的任务:

public IAsyncResult BeginInvoke(Delegate method);

BeginInvoke/RunAsync方法是将这个 Delegate推送到UI线程的消息队列中,这个消息队列也就是前面提到的鼠标,键盘事件等队列。

Invoke方法也是推送delegate到消息队列,但还会一直阻塞到此delegate被UI线程处理为止。所以一般来说我们还是用BeginInvoke/RunAsync方法。

对应app来说,我们可以将其想象为一下的伪代码:

while (!thisApplication.Ended)
{
wait for something to appear in message queue
Got something: what kind of message is it?
Keyboard/mouse message -> fire an event handler
User BeginInvoke message -> execute delegate
User Invoke message -> execute delegate & post result
}

那接下来我们用winform来demo一下:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        new Thread(work).Start();
    }

    void work()
    {
        Thread.Sleep(5000);
        UpdateMessage("july Luo thread Test");
    }

    void UpdateMessage(string message)
    {
        Action action = () => lblJulyLuo.Text = message;
        this.BeginInvoke(action);
    }
}

方法2

在 System.ComponentModel命名空间中,有 SynchronizationContext抽象类,此类也可以处理线程marshal。

在wpf,metro, winform中都定义了此类的子类,而且可以用SynchronizationContext.Current获取,然后调用Post方法,可以理解为将其他线程的任务post到UI线程中。

一下为demo:

public partial class Form1 : Form
{
    SynchronizationContext _uiSyncContext;

    public Form1()
    {
        InitializeComponent();
        _uiSyncContext = SynchronizationContext.Current;
    }

    void work()
    {
        Thread.Sleep(5000);
        UpdateMessage("july Luo thread Test");
    }

    void UpdateMessage(string message)
    {
        _uiSyncContext.Post(_ => lblJulyLuo.Text = message, null);
    }
}

SynchronizationContext类还有一个Send方法,和我们上面提到的Invoke方法的作用一致。

当然了还有BackgroundWorker类,此类在内部用了SynchronizationContext,所以其也可在其他线程中更新UI线程。

总结

富客户端中UI线程一直会处理着消息循环,无论使用那种方法都是将其推送到消息队列中以便UI线程处理。

时间: 2024-11-05 15:18:34

富客户端 wpf, Winform 多线程更新UI控件的相关文章

(转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例

http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-demo 在C# WINFORM的开发中,难免会遇到多线程的开发以提高程序的执行效率.自己刚才开始在做多线程的开发时也遇到了很多这方面的问题,比如:如何使用并实现多线程功能.跨线程更新UI控件等问题.还记得最初使用的是System.Threading命名空间下的Thread类来实现的: C# 1 2 3

WinForm/Silverlight多线程编程中如何更新UI控件的值

单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么做,比如: private void btnSet_Click(object sender, EventArgs e){        Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));    //当然也可以用匿名委托写成

C# Winform 跨线程更新UI控件常用方法总结(转)

出处:http://www.tuicool.com/articles/FNzURb 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常.处理跨线程更新Winform UI控件常用的方法有4种: 1. 通过UI线程的SynchronizationContext的Post/Send方法更新: 2. 通过UI控件的Invoke/BegainInvoke方法更新: 3. 通过BackgroundWorker取代Thre

C# Winform 跨线程更新UI控件常用方法汇总

C# Winform 跨线程更新UI控件常用方法汇总 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常.处理跨线程更新Winform UI控件常用的方法有4种:1. 通过UI线程的SynchronizationContext的Post/Send方法更新:2. 通过UI控件的Invoke/BeginInvoke方法更新: 3. 通过BackgroundWorker取代Thread执行异步操作:4. 通过设置窗体

C#子线程更新UI控件的方法总结

http://blog.csdn.net/jqncc/article/details/16342121 在winform C/S程序中经常会在子线程中更新控件的情况,桌面程序UI线程是主线程,当试图从子线程直接修改控件属性时会出现“从不是创建控件的线程访问它”的异常提示. 跨线程更新UI控件的常用方法有两种: 1.使用控件自身的invoke/BeginInvoke方法 2.使用SynchronizationContext的Post/Send方法更新 1.使用控件自身的invoke/BeginIn

C# WPF 使用委托修改UI控件

近段时间在自学WPF,是一个完全不懂WPF的菜鸟,对于在线程中修改UI控件使用委托做一个记录,给自已以后查询也给需要的参考: 界面只放一个RichTextBox,在窗体启动时开起两个线程,调用两个函数,每隔1秒写一次当前时间 一 界面XAML如下: <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&qu

网络操作不能直接写在主线程中 以及 为什么不能在子线程中更新UI控件的属性

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //注意: 所有网络操作不能直接写在主线程中 因为所有的网络操作都是耗时的,如果加载到主线程中,会导致与用户的交互出现问题 ,所以要加载到子线程中 // [self loadImage]; [self performSelectorInBackground:@selector(loadImage) withObject:nil]; } //加

winform中如何在多线程中更新UI控件--ListView实时显示执行信息

1.在winform中,所有对UI的操作,都得回到UI线程(主线程)上来,才不会报错 线程间操作无效: 从不是创建控件的线程访问它. 2.在winform中,允许通过Control.invoke对控件进行操作.如下代码: private void btnTest_Click(object sender, EventArgs e) { CheckA(); } private void CheckA() { System.Threading.ThreadPool.QueueUserWorkItem(

多线程操作UI控件——DataGridView假死现象

在多线程编程中,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常.这是微软为了保证线程安全以及提高代码的效率所做的改进,但是也 给大家带来很多不便. 今天我就遇到了一个类似的问题,对DataGridView指定 DataSource 来填充数据,更新数据的时候,会导致DataGridView出现假死,显示错误或者滚动条无法显示的问题,在保证了DataGridView的ScrollBars设置为了Both,数据量大于DataGridView显示的的范围,而且没有冻结列