如何从另一个线程更新 GUI 上的文本框 [英] How to update textbox on GUI from another thread
问题描述
我是 C# 新手,正在尝试制作一个简单的客户端服务器聊天应用程序.
I'm new with C# and I'm trying to make a simple client server chat application.
我的客户端 Windows 窗体上有 RichTextBox,我正在尝试从另一个类中的服务器更新该控件.当我尝试执行此操作时,出现错误:跨线程操作无效:从创建它的线程以外的线程访问控制 textBox1".
I have RichTextBox on my client windows form and I am trying to update that control from server which is in another class. When I try to do it I get the error: "Cross-thread operation not valid: Control textBox1 accessed from a thread other than the thread it was created on".
这里是我的 Windows 窗体的代码:
Here the code of my Windows form:
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...
";
}
那怎么做呢?如何从另一个线程更新文本框控件?
So how to do that? How can I update the textbox control from another thread?
我正在尝试使用 .net 远程处理制作一些基本的聊天客户端/服务器应用程序.我想将 Windows 窗体客户端应用程序和控制台服务器应用程序制作为单独的 .exe 文件.我在这里尝试从客户端调用服务器函数 AddUser,我想 AddUser 函数更新我的 GUI.我已经按照你的建议修改了 Jon 的代码,但现在我得到了这个异常,而不是跨线程异常......SerializationException:Assembly 中的类型主题未标记为可序列化".
I'm trying to make some basic chat client/server application using .net remoting. I want to make windows form client application and console server application as separate .exe files. Here im trying to call server function AddUser from client and i want to AddUser function update my GUI. Ive modified code as you suggested Jon but now instead of cross-thread exception i've got this exception ... "SerializationException: Type Topic in Assembly is not marked as serializable".
我会在下面发布我的全部代码,我会尽量保持简单.
欢迎任何建议.非常感谢.
Ill post my whole code bellow, will try to keep it simple as possible.
Any suggestion is welcome. Many thanks.
服务器:
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 表单客户端:
// 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);
推荐答案
您需要使用 BackgroundWorker
或 Control
.Invoke
/开始调用
.匿名函数 - 匿名方法 (C# 2.0) 或 lambda 表达式 (C# 3.0) 使这比以前更容易.
You need to either use BackgroundWorker
, or Control
.Invoke
/BeginInvoke
. Anonymous functions - either anonymous methods (C# 2.0) or lambda expressions (C# 3.0) make this easier than it was before.
就您而言,您可以将代码更改为:
In your case, you can change your code to:
public bool AddUser(string user, RichTextBox textBox1, List listBox1)
{
MethodInvoker action = delegate
{ textBox1.Text += "Connected to server...
"; };
textBox1.BeginInvoke(action);
}
需要注意的几点:
- 为了符合 .NET 约定,这应该被称为
AddUser
- 您不需要通过引用传递文本框或列表框.我怀疑您不太明白
ref
的真正含义 - 请参阅 my关于参数传递的文章了解更多详情. Invoke
和BeginInvoke
的区别在于BeginInvoke
不会等待委托在 UI 线程上被调用才继续- 所以AddUser
可能会在文本框实际更新之前返回.如果您不想要这种异步行为,请使用Invoke
.- 在许多示例(包括我的一些示例!)中,您会发现人们使用
Control.InvokeRequired
来查看他们是否需要调用Invoke
/BeginInvoke代码>.在大多数情况下,这实际上是过度的 - 调用
Invoke
/BeginInvoke
没有真正的危害,即使您不需要,而且通常无论如何,处理程序将只从非 UI 线程中调用.省略检查使代码更简单. - 您还可以使用
BackgroundWorker
正如我之前提到的;这特别适用于进度条等,但在这种情况下,保留当前模型可能同样容易.
- To conform with .NET conventions, this should be called
AddUser
- You don't need to pass the textbox or listbox by reference. I suspect you don't quite understand what
ref
really means - see my article on parameter passing for more details. - The difference between
Invoke
andBeginInvoke
is thatBeginInvoke
won't wait for the delegate to be called on the UI thread before it continues - soAddUser
may return before the textbox has actually been updated. If you don't want that asynchronous behaviour, useInvoke
. - In many samples (including some of mine!) you'll find people using
Control.InvokeRequired
to see whether they need to callInvoke
/BeginInvoke
. This is actually overkill in most cases - there's no real harm in callingInvoke
/BeginInvoke
even if you don't need to, and often the handler will only ever be called from a non-UI thread anyway. Omitting the check makes the code simpler. - You can also use
BackgroundWorker
as I mentioned before; this is particularly suited to progress bars etc, but in this case it's probably just as easy to keep your current model.
有关此主题和其他线程主题的更多信息,请参阅我的线程教程或
For more information on this and other threading topics, see my threading tutorial or Joe Albahari's one.
这篇关于如何从另一个线程更新 GUI 上的文本框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!