从Main(UI)线程更改另一个线程中的控件属性

时间:2012-10-23 00:38:56

标签: c# multithreading

我现在正在使用namedpipe和C#实现网络程序。在服务器程序的main方法中,我启动了一个名为“ReadingMessage”的线程,它从客户端程序中读取消息。在此线程中,它需要根据客户端返回的消息更改控件的属性(文本框的文本,标签的位置等)。因此,它在此过程中存在交叉线程错误(“UI线程之间的ReadingMesssage”)。我发现有两种方法可以解决这个问题

  1. 使用Delegate
  2. 调用控件
  3. Form.CheckForIllegalCrossThreadCalls = false;
  4. 我测试了它们,但有一个问题。当表单首次启动时,控件的属性会更改而不会出现任何错误。当窗体关闭并再次作为第二次启动时,控件的属性不会更改,并且不会显示任何错误消息。我将断点捕获到指示控件属性发生更改的代码中。代码运行顺利,但问题仍然存在。

    我不知道那是什么问题。因此,如果有人知道这个问题,请建议我......

    更新:(在我的第一个问题中发布代码并纠正一些错误)

    线程不在服务器程序的主方法中。我错了,因为我想简短地描述一下我的问题。现在,我将更详细地描述我的问题。服务器程序的主要形式中有一个按钮单击事件。

    //Main Form
    public partial class MainForm : Form
    {
        private void btnButton_Click(object sender, EventArgs e)
        {
           AnotherForm aform = new AnotherForm(); //This is the form where threading run
           aform.show();    
        }
    }
    
    //AnotherForm
    public partial class AnotherForm : Form
    {
        private void CargoLoading_Load(object sender, EventArgs e)
        {
            (new System.Threading.Thread(readingMessage)).Start(myPipe);    
        }
    }
    
    private void readingMessage(Object myPipeObject) //Read the messages send from the client machine
    {
         while (true)
         {
             //Code of serverstream
             SetTextofTextBox1("AnyText"); //Code where the properties of control are instructed to change
         }
    }
    
    private void SetTextofTextBox1(string text)
    {
         if (this.txtTextBox1.InvokeRequired) //the debugger return false at second time of this form is opened
         {
             SetTextCallback d = new SetTextCallback(SetTextofTextBox1);
             this.Invoke(d, new object[] { text });
         }
         else
         {
              this.txtTextBox1.Text = text;
              this.txtTextBox1.Update();
         }
    }
    

    这就是出现问题的所有代码。当“AnotherForm”第一次打开时,没关系。但它第二次再次打开,控件的属性(TextBox1的文本)不受影响而且没有改变。在那里,我注意到调试器第二次返回false Control.InvokeRequired。对于所有长期问题都非常抱歉,因为我是程序员的新手。

    仍然期待你的建议......

    更新2:

    最后我尝试了所有的方法,但它并没有出来最好的解决方案。因此,我想建议所有人只要避免这种情况,并在遇到像我这样的情况时,在另一个概念中重写一些代码。

2 个答案:

答案 0 :(得分:2)

在后台线程中,您应该使用Control.InvokeRequiredControl.Invoke以使UI线程执行更新UI的代码。

只允许UI线程“触摸”控件属性,因此后台线程无法更新它们。

更具体地说,如果您对更多细节感兴趣:在Windows中,每个表单和控件通常都有一个或多个与之关联的HWND。 HWND是表示Windows窗口的Windows操作系统对象的句柄。

每个HWND始终与特定线程(创建它的线程)相关联。发送到该HWND的任何Windows消息都将到达该线程的消息队列,并且必须由该线程的消息循环处理。

当您对Controls方法进行看似无辜的调用时,这些方法的实现有时可能会破坏并创建HWND(例如,为Menu控件创建子菜单或向工具栏控件添加按钮)。现在,如果你要从不同的线程进行这样的调用,你最终会得到属于不同线程的HWND,并且你的控件消息的处理将被破坏。

由于这个原因,除了创建控件的HWND的线程(例如,调用了`Control.CreateControl()的线程)之外的任何线程都禁止对控件方法的所有访问。

在底线中,对于大多数简单的应用程序,主线程是唯一创建窗口的线程,因此您应该仅从主线程触摸表单和控件。

答案 1 :(得分:0)

请发布代码。特别是用于打开和关闭窗口的代码。

从您的描述中我猜测您在运行后台线程时在表单上运行Application.Run()循环。

然后,您可能正在关闭表单并打开一个新表单,而不运行消息循环。所以Control.Invoke返回,但UI线程没有调用回调(不抽取消息)。

在没有看到代码的情况下,这只是猜测。