我是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);
解决方法
在您的情况下,您可以将代码更改为:
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 tutorial或Joe Albahari’s one.