如何从C#中的另一个线程更新GUI上的文本框[复制]

参见英文答案 > How to update the GUI from another thread in C#?44
我是C#的新手,我正在尝试制作一个简单的客户端服务器聊天应用程序.

我的客户端窗体中有RichTextBox,我正在尝试从另一个类中的服务器更新该控件.当我尝试这样做时,我得到错误:“跨线程操作无效:控制textBox1从一个线程以外的线程而被创建”.

这里我的Windows窗体的代码

private Topic topic;  
public RichTextBox textBox1;  
bool check = topic.addUser(textBoxNickname.Text,ref textBox1,ref listitems);

主题类:

public class Topic : MarshalByRefObject  
{  
    //Some code
 public  bool addUser(string user,ref RichTextBox textBox1,ref List<string> listBox1)  
 {  
     //here i am trying to update that control and where i get that exception  
     textBox1.Text += "Connected to server... \n";  
}

那怎么办呢?如何从另一个线程更新文本框控件?

我试图使用.net远程处理使一些基本的聊天客户端/服务器应用程序.
我想使窗体客户端应用程序和控制台服务器应用程序作为单独的.exe文件.这里我试图从客户端调用服务器功能AddUser,我想要AddUser函数更新我的GUI.我修改代码,因为你建议Jon但现在,而不是跨线程异常我有这个异常…“SerializationException:类型主题在程序集没有标记为可序列化”.

不幸发表我的整个代码,将尽可能保持简单.
欢迎任何建议.非常感谢.

服务器:

namespace Test
{
    [Serializable]
    public class Topic : MarshalByRefObject
    {
        public bool AddUser(string user,RichTextBox textBox1,List<string> listBox1)
        {
            //Send to message only to the client connected
            MethodInvoker action = delegate { textBox1.Text += "Connected to server... \n"; };
            textBox1.BeginInvoke(action);
            //...
            return true;
        }

        public class TheServer
        {
            public static void Main()
            {

                int listeningChannel = 1099;

                BinaryServerFormatterSinkProvider srvFormatter = new BinaryServerFormatterSinkProvider();
                srvFormatter.TypeFilterLevel = TypeFilterLevel.Full;

                BinaryClientFormatterSinkProvider clntFormatter = new BinaryClientFormatterSinkProvider();

                IDictionary props = new Hashtable();
                props["port"] = listeningChannel;

                HttpChannel channel = new HttpChannel(props,clntFormatter,srvFormatter);
                // Register the channel with the runtime            
                ChannelServices.RegisterChannel(channel,false);
                // Expose the Calculator Object from this Server
                RemotingConfiguration.RegisterWellKNownServiceType(typeof(Topic),"Topic.soap",WellKNownObjectMode.Singleton);
                // Keep the Server running until the user presses enter
                Console.WriteLine("The Topic Server is up and running on port {0}",listeningChannel);
                Console.WriteLine("Press enter to stop the server...");
                Console.ReadLine();
            }
        }
    }

}

Windows窗体客户端:

// Create and register a channel to communicate to the server
        // The Client will use the port passed in as args to listen for callbacks

        BinaryServerFormatterSinkProvider srvFormatter = new BinaryServerFormatterSinkProvider();
        srvFormatter.TypeFilterLevel = TypeFilterLevel.Full;
        BinaryClientFormatterSinkProvider clntFormatter = new BinaryClientFormatterSinkProvider();
        IDictionary props = new Hashtable();
        props["port"] = 0;

        channel = new HttpChannel(props,srvFormatter);
        //channel = new HttpChannel(listeningChannel);

        ChannelServices.RegisterChannel(channel,false);
        // Create an instance on the Remote Server and call a method remotely
        topic = (Topic)Activator.Getobject(typeof(Topic),// type to create
        "http://localhost:1099/Topic.soap" // URI
        );


        private Topic topic;
        public RichTextBox textBox1;
        bool check = topic.addUser(textBoxNickname.Text,textBox1,listitems);

解决方法

您需要使用BackgroundWorker或Control.Invoke / BeginInvoke.匿名函数 – 匿名方法(C#2.0)或lambda表达式(C#3.0)使之比以前更容易.

在您的情况下,您可以将代码更改为:

public bool AddUser(string user,List listBox1)
{
    MethodInvoker action = delegate
         { textBox1.Text += "Connected to server... \n"; };
    textBox1.BeginInvoke(action);
}

需要注意的几件事情

>为了符合.NET约定,这应该称为AddUser
>您不需要通过引用传递文本框或列表框.我怀疑你不太明白ref是什么意思 – 有关更多详情,请参阅my article on parameter passing.
> Invoke和BeginInvoke之间的区别是,BeginInvoke不会等待委托在UI线程上继续调用,所以AddUser可能会在文本框实际更新之前返回.如果您不想要异步行为,请使用Invoke.
>在许多示例(包括我的一些)中,您会发现使用Control.Invokerequired的人可以查看他们是否需要调用Invoke / BeginInvoke.这在大多数情况下实际上是过度的 – 即使不需要调用Invoke / BeginInvoke也没有真正的危害,并且通常只有处理程序才会从非UI线程调用.省略检查使代码更简单.
>你也可以像前面提到的那样使用BackgroundWorker;这特别适合进度条等,但在这种情况下,保持当前模型可能很简单.

有关此和其他线程主题的更多信息,请参阅my threading tutorialJoe Albahari’s one.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...