平常在做Winform的应用程序中,可能很少有人不用到多线程吧?一般说来,我们的工作线程多和界面的线程(主线程)不在一个线程中,道理也很简单,在一个线程中的话,较长时间等待的工作会阻塞界面线程,影响界面显示。下文中我提到的“工作线程”均指从界面线程引发的新的线程
那么,相信大家也经常会遇到在工作线程中需要更新界面元素的情况吧,比如,除错了我们要Msgbox一个出错信息,可能还要在Statusbar中显示一些东西(进度等等),那么,遇到这种情况你在工作线程中是怎么写的呢?是直接写:Msgbox("error")这样吗?以前我也一直这样做。
后来看了欧严亮的WebCast,我知道这样做是不准确的。当工作线程需要更新界面线程的元素时,应使用界面线程来更新。具体来说,类似下面这样:
比如我们在工作线程中调用方法ShowError(byval msg as String)来显示错误信息,那么这个ShowError方法不应该只有一句Msgbox(msg),而是应该这样:
Private Delegate Sub ShowErrDelegate(ByVal msg As String)
Protected Sub ShowErr(ByVal msg As String)
        If InvokeRequired Then
            BeginInvoke(New ShowErrDelegate(AddressOf ShowErr), New Object() {msg})
            Return
        End If
        MsgBox(msg, MsgBoxStyle.Exclamation, "Error")

Me.Cursor = System.Windows.Forms.Cursors.Default
End Sub

这一段代码先判断调用是否是从工作线程中来的,即if Invokerequired的语句(当然,这里用的是me.invokerequired,你也可以用界面线程中的任一个控制,比如me.TextBox1.invokerequired这样),如果不是的话,那么,会通过界面线程的Delegate来调用此方法,当然,这个Delegate是指向自己的。看起来有点像递归,其实不是的。他的大概意思就是,如果不是从界面线程来调用我的,那么,我使用界面线程来调用我。

虽然我把代码中的相应部分改了,改成用界面线程来调用,但是我以前真的觉得多此一举,因为直接在工作线程中更新界面一样运行得很好啊,为什么要这么麻烦?这究竟只是一种规范,还是必不可少?

后来终于通过一个应用明白了其中的意义。

我的应用程序后来做多语言版了,我参考了一下msdn,决定使用资源文件的方式来实现多语言。dotnet中,系统可根据当前线程的CultrueInfo信息,决定使用哪个资源文件。于是乎,在我的界面线程中,我用了这样的语句来设定其CultureInfo信息:
If Language = GlobalVariableModule.Lang.English Then
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US")
        Else
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture("zh-CN")
End If
然后自然,有些语句得改了,比如在工作线程中,我这样调用ShowError方法:
ShowError(RscMgr.GetString("ERROR"))        'RscMgr为一ResourceManager

按理说,如果我的language设为中文,应显示中文的错误信息,设为英文应显示英文错误信息,其实不然,这个调用无论在什么情况下,都显示default的错误信息,即英文的。为什么呢?因为我们在工作线程中调用了ResourceManager来得到错误信息对应的字符串,但是在工作线程中,即cultureInfo其实是为英文的。所以,传给ShowError的msg,其实是英文的错误信息。
我马上想到了补救方法,两个:
1、修改ShowError的参数,改为rscId,即,我传一个resourceid过去,在本例中,为"ERROR",并且,在ShowError方法中,我们不再直接msgbox参数,而是MsgBox(RscMgr.GetString(rscId)),这样就使用了界面线程来得到错误信息,是会正常显示中文的错误信息的。当然,如果你试着把ShowError方法中的invokerequired语句部分去掉,还是不能正常显示中文错误信息,道理前面讲过,这样是用工作线程来调用了些方法。到了这里,你也明白了为什么一定要用界面线程来更新界面元素了吧,这只是其中一个例子。
2、在工作线程中设置CultrueInfo信息,即在工作线程中加入下面的语句:
If Language = GlobalVariableModule.Lang.English Then
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US")
        Else
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture("zh-CN")
End If
然后还是使用原来的ShowError方法。

我一开始想用第1种方法,不知道为什么,总觉得不应该在工作线程中加入太多可能和界面的工作。但其实第一种方法很不容易行得通,因为我的错误信息不会是简单的String,不是简单的通过RscMgr.GetString(rscId)就可以得到的,而是有相关的其它信息,比如,错误出现在什么位置等等,这时我的错误信息可能是以下面的方式组装起来的:String.Format(RscMgr.GetString(rscId),lineNo),其中,lineNo表示错误出现的行数,而资源文件中存放的错误信息是这样的格式:"Error in line {0}"。在这种情况下,因为lineNo这样的值来自于工作线程,所以错误信息只能再工作线程里组装完成,当然也可以把lineNo传递给ShowError方法,但这样会导致ShowError方法的重载版本太多,而且和特定应用偶合太紧。
所以不得已,还是得使用第二种方法。

当然,对于我最前面说的情况,在界面线程的方法中要判断对其的调用是否来自主线程,也可以不这样做。取而代之,我们可以在工作线程中引发事件,而界面线程的更新界面方法来响应这个事件,这样也可以达到“用界面线程来更新界面元素”的目的。

无论是哪种方法,在当前我遇到的情况下,如果你要在工作线程中组装错误信息,那么只能把工作线程的CultrueInfo也设置一下。事实上我不太喜欢这种的设计方式,不知道大家在遇到类似情况时有什么好的设计方法吗?一起交流一下吧。

转载于:https://www.cnblogs.com/yinh/archive/2005/05/25/162150.html

有感于“用界面线程去更新界面元素”相关推荐

  1. android 线程 界面,android开发教程之子线程中更新界面

    每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联. Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间 ...

  2. Android中Handler的使用方法——在子线程中更新界面

    本文主要介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Ha ...

  3. Android--Handler的使用方法:在子线程中更新界面

    版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/chaoyu168/article/details/50914021 本文主要介绍Android的Handl ...

  4. Qt主线程和工作线程更新界面问题

    Qt在运行时会开启一个主线程,如果没有开启工作线程的话,所有界面上的操作都是在主线程,包括更新界面或者处理数据等操作.大家都知道如果处理数据比较多的话,最好是在单独开启一个线程来处理数据,这样就不会影 ...

  5. Qt界面加载网络摄像头,并实时显示,两种更新界面的方法

    Qt界面加载网络摄像头,并实时显示,两种更新界面的方法 1.Qt界面加载网络摄像头,并实时显示,根据头文件中的宏定义来区分使用哪种方式.①Qt信号槽更新界面,②c语言回调方式更新界面 2.使用Open ...

  6. VC++中多线程学习(MFC多线程)一(线程的创建、线程函数如何调用类成员呢?如何调用主对话框的成员?、MFC中的工作线程和界面线程的区别)

    这里废话不多讲了,因为项目原因,需要开启线程进行处理,在不了解线程的情况下,直接百度一下,然后就使用了,结果可想而知,出现了异常,所以花了一天时间系统学习一下多线程,这里主要是针对win32编程方面的 ...

  7. c#后台线程更新界面

    参考文章<C# 线程更新UI界面> 主窗口 public frmMain(){InitializeComponent();}/// <summary>/// 主窗口加载事件// ...

  8. 解决Python Socket UDP端口侦听线程内更新UI界面卡顿、奔溃的问题

    网络读卡器介绍:WIFI无线网络RFID云读卡器远程网络开关物流网NB-IoT4G刷卡阅读器-淘宝网 (taobao.com) Python使用PyQt5做UI界面,开启线程侦听UDP端口,端口接收到 ...

  9. C#-WinForm跨线程修改UI界面

    背景 在我做WinForm开发的过程中,经常会遇到耗时操作或阻塞操作.他们会引发软件的卡顿甚至假死,严重影响软件的使用.因此,这类耗时或阻塞的操作一般都会使用异步的方式去执行,不影响主线程(UI线程) ...

最新文章

  1. MHA故障切换和在线手工切换原理
  2. mqtt js 中乱码_Vue.js 中的 v-cloak 指令——Vue学习之路
  3. 初识Mysql(part15)--我需要知道的4条Mysql语句之操作表
  4. Queue(队列 C++模版实现)
  5. EF Code First学习笔记:数据库创建(转)
  6. 程序员编程艺术:第三章续、Top K算法问题的实现
  7. python官网的软件-python软件怎么用
  8. Visual Assist X V10.4.1626.0 不错的软件,让VC++也能自动提示
  9. AD域服务器中批量创建帐户
  10. Qt中,edit窗口输出的几种格式
  11. nginx 访问频率限制配置
  12. hbase 和Hadoop的国内下载镜像
  13. MMC多电平变换器matlab/simulink模型
  14. Android studio上音频文件格式问题
  15. STM32 - L4系列芯片手册: LTDC功能
  16. O365(世纪互联)SharePoint 之使用列表库发布新闻
  17. C语言:输入一个不多于5位的正整数,要求1:求出它是几位数; 要求2:分别打印出每一位数; 要求3:按逆序打印出各位数字;
  18. 系列好文(2)——《百岁感言》杨绛
  19. win7如何启用计算机的无线功能,win7如何开启无线功能
  20. 如何培养你的数据分析思维?

热门文章

  1. 微投抖的1080_1080P不到三千元 微投价格战竟如此激烈
  2. VMware SDS 什么是VSAN?? VSAN的体系结构 (含VSAN 6.0、6.1版的新内容)
  3. 批量重置指定域用户密码
  4. Docker 学习总结(71)—— Docker、Docker Compose、Docker Swarm 之间的区别
  5. 项目管理学习总结(2)——需求收集和管理
  6. Netty学习总结(3)——Netty百万级推送服务
  7. 数人云CTO解读Docker 1.12和金融业容器化
  8. 换ip_换IP对云手机来说是好事吗?
  9. python输入错误提示再次输入_Python实现用户登录并且输入错误三次后锁定该用户...
  10. 组装r730服务器,戴尔poweredge r730服务器配置及系统安装详解教程