How to update textbox on GUI from another thread
本问题已经有最佳答案,请猛点这里访问。
我是C的新手,我正在尝试制作一个简单的客户机-服务器聊天应用程序。
我的客户机Windows窗体上有RichTextBox,我正在尝试从另一个类中的服务器更新该控件。当我尝试这样做的时候,我得到了一个错误:"跨线程操作无效:从创建它的线程以外的线程访问控件textbox 1"。
这里是我的Windows窗体的代码:
1 2 3 | private Topic topic; public RichTextBox textbox1; bool check = topic.addUser(textBoxNickname.Text, ref textbox1, ref listitems); |
主题类:
1 2 3 4 5 6 7 8 9 | 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... "; } |
号
那么怎么做呢?如何从其他线程更新文本框控件?
我正在尝试使用.NET远程处理创建一些基本的聊天客户端/服务器应用程序。我要将Windows窗体客户端应用程序和控制台服务器应用程序设置为单独的.exe文件。在这里,我试图从客户端调用服务器函数adduser,我想要adduser函数更新我的GUI。我按照您的建议修改了代码,但是现在我得到了这个异常,而不是跨线程异常……"序列化异常:程序集中的类型主题未标记为可序列化"。
我会把我的全部代码贴在下面,尽量保持简单。欢迎提出任何建议。非常感谢。
服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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... "; }; 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窗体客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 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, clntFormatter, 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); |
。
您需要使用
在您的情况下,您可以将代码更改为:
1 2 3 4 5 6 7 | public bool AddUser(string user, RichTextBox textBox1, List listBox1) { MethodInvoker action = delegate { textBox1.Text +="Connected to server... "; }; textBox1.BeginInvoke(action); } |
需要注意的几点:
- 为了符合.NET惯例,这应该称为
AddUser 。 - 您不需要通过引用传递文本框或列表框。我怀疑你不太明白
ref 的真正含义——更多细节请参阅我关于参数传递的文章。 Invoke 和BeginInvoke 的区别在于BeginInvoke 在继续之前不会等待在UI线程上调用委托,因此AddUser 可能会在实际更新文本框之前返回。如果您不希望使用异步行为,请使用Invoke 。- 在许多样品中(包括我的一些样品!)你会发现人们使用
Control.InvokeRequired 来查看是否需要呼叫Invoke 或BeginInvoke 。在大多数情况下,这实际上是杀伤力过大的——即使您不需要调用Invoke /BeginInvoke ,也不会造成真正的伤害,而且通常只从非UI线程调用处理程序。省略检查使代码更简单。 - 您也可以像我前面提到的那样使用
BackgroundWorker ;这特别适合进度条等,但在这种情况下,保持当前模型可能同样容易。
有关此主题和其他线程主题的详细信息,请参阅我的线程教程或JoeAlbahari的教程。
使用调用方法
1 2 3 4 5 6 | // Updates the textbox text. private void UpdateText(string text) { // Set the textbox text. yourTextBox.Text = text; } |
现在,创建一个与先前定义的方法具有相同签名的委托:
1 | public delegate void UpdateTextCallback(string text); |
在线程中,可以在rtextbox上调用invoke方法,传递要调用的委托以及参数。
1 2 |