C# Winform 状态栏StatusStrip跨线程访问问题

Jasongrass 2014-08-22 02:42:30
平台:VS2013,C#,winform 窗体程序
控件:StatusStrip 状态栏,名称 statusStrip
问题背景:
状态栏上有一个 ToolStripStatusLabel 控件,用于显示系统当前时间,控件名称为: toolStripStatusLabel_DateTime。
在程序中,有一个 System.Timers.Timer 的线程,用于实时更新时间显示,每 500ms运行一次。
代码:

private void OnTimer_ShowTime(Object source, ElapsedEventArgs e)
{
//toolStripStatusLabel_DateTime.Text = DateTime.Now.ToLongTimeString();
statusStrip.Items["toolStripStatusLabel_DateTime"].Text = DateTime.Now.ToLongTimeString();
}

两行代码分别是调用 toolStripStatusLabel_DateTime 的两种方式,都会出现下面提到的问题。
正常情况:


问题描述:
当在窗体右下角按住鼠标左键拖动,改变窗体大小时:
1、若拖动速度很慢,状态栏的重绘能跟上窗体的变化,就不会出现问题。
2、若拖动速度较快,状态栏的重绘跟不上窗体的变化(如下图2,状态栏在拖动的过程中不见了),则会引发异常,提示:线程间操作无效,从不是创建控件“statusStrip ”的线程访问它。
图2 :状态栏在拖动过程中显示异常:


图3:异常提示:


问题:
1、这里的线程间操作,指的是哪个线程操作了 statusStrip ,应该不是 那个更新时间的Timer线程吧,不然为什么只有拖动窗体时才会这样。
2、怎么解决这一问题:
2.1 我试着用 try catch,但catch中为空的方式,表面上可以解决问题,但是只是表面上。
...全文
1143 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
threenewbee 2014-08-23
  • 打赏
  • 举报
回复
用Invoke委托更新: private void OnTimer_ShowTime(Object source, ElapsedEventArgs e) { this.Invoke(new Action(() => { statusStrip.Items["toolStripStatusLabel_DateTime"].Text = DateTime.Now.ToLongTimeString(); })); }
xian_wwq 2014-08-23
  • 打赏
  • 举报
回复
Control.CheckForIllegalCrossThreadCalls = false; 个人不推荐使用。 独立工作线程与UI交互必然要用委托。 委托的例子可以参考:

public class MyForm : System.Windows.Forms.Form {
            
    //UI 元素
    private Label lblStatus;
    
    private ProgressBar progressBar1;

    //Delegate
    private delegate void MyProgressEventsHandler(
        object sender, MyProgressEvents e);

    
     private void UpdateUI(object sender, MyProgressEvents e) {
        lblStatus.Text = e.Msg;
        myProgressControl.Value = e.PercentDone;
    }

   //ShowProgress 现在可以记录为可从任何线程调用的公共方法。
    public void ShowProgress(string msg, int percentDone) 
   {
	if(InvokeRequired)
	{
	   System.EventArgs e = new MyProgressEvents(msg, percentDone);
           object[] pList = { this, e };

           BeginInvoke(new MyProgressEventsHandler(UpdateUI), pList);
	}
	else
        {
	    UpdateUI(this, new MyProgressEvents(msg,
            PercentDone)); 	
        }
       
    }

     private void btnStart_Click(object sender, EventArgs e)
     {
      //启动线程
      Thread t = new Thread(new ParameterizedThreadStart(RunsOnWorkerThread));
      t.IsBackground = true;
      t.Start(input);
     }

    //线程执行函数
    private void RunsOnWorkerThread() 
    {
        int i = 0;   
        while(...) //loop      
        {  
          DoSomethingSlow();
          ShowProgress("test",i);
          ++i;
        }
    }
   
}
Jasongrass 2014-08-23
  • 打赏
  • 举报
回复
设置

private void Form1_Load(object sender, EventArgs e)
        {
            Control.CheckForIllegalCrossThreadCalls = false;
        }
可以解决问题,但是还是没理解为什么。 1、究竟是哪个线程调用了 StatusStrip 状态栏,而引发的异常。 2、跨线程调用是怎么回事,微软好像不建议关闭跨线程访问检查的。
道玄希言 2014-08-23
  • 打赏
  • 举报
回复
http://blog.csdn.net/jinjazz/article/details/1927126 这个资料看是否有用了~
Jasongrass 2014-08-22
  • 打赏
  • 举报
回复
引用 2 楼 Z65443344 的回复:
除非你还想给用户看当前的毫秒是多少,否则少于1秒的刷新都是毫无意义的.
暂时不要纠结这些非技术细节问题好么…… 我只是想搞明白为什么,不是绕开这个问题。如果只是绕开,try catch足够了。 另外,主要是我的程序里面有其它任务线程,也涉及到调用状态栏上 ToolStripStatusLabel控件的问题,所以,还是搞明白的好。
於黾 2014-08-22
  • 打赏
  • 举报
回复
除非你还想给用户看当前的毫秒是多少,否则少于1秒的刷新都是毫无意义的.
於黾 2014-08-22
  • 打赏
  • 举报
回复
直接从工具箱里拖个timer过来,这里根本没必要用线程啊 而且时间也不要500ms,1000ms就行了 时间本来就是1秒变一次,你半秒一刷新,刷新了给谁看啊

111,120

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧