如何从另一个线程在GUI上更新文本框 [英] How to update textbox on GUI from another thread

查看:82
本文介绍了如何从另一个线程在GUI上更新文本框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是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... \n";  
}

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

So how to do that? How can I update the textbox control from another thread?

我正在尝试使用.net远程处理来制作一些基本的聊天客户端/服务器应用程序. 我想将Windows窗体客户端应用程序和控制台服务器应用程序制作为单独的.exe文件.在这里,我试图从客户端调用服务器函数AddUser,我想AddUser函数更新我的GUI.我已经按照您建议的Jon修改了代码,但现在有了跨线程异常而不是跨线程异常... "SerializationException:程序集中的Type主题未标记为可序列化" .

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... \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, 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);

推荐答案

您需要使用BackgroundWorkerControl.Invoke/BeginInvoke.匿名函数-匿名方法(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... \n"; };
    textBox1.BeginInvoke(action);
}

一些注意事项:

  • 要符合.NET约定,应将其称为AddUser
  • 您无需通过引用传递文本框或列表框.我怀疑您不太了解ref的真正含义-请参见我有关参数传递的文章了解更多详情.
  • InvokeBeginInvoke之间的区别是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 and BeginInvoke is that BeginInvoke won't wait for the delegate to be called on the UI thread before it continues - so AddUser may return before the textbox has actually been updated. If you don't want that asynchronous behaviour, use Invoke.
  • In many samples (including some of mine!) you'll find people using Control.InvokeRequired to see whether they need to call Invoke/BeginInvoke. This is actually overkill in most cases - there's no real harm in calling Invoke/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.

有关此主题和其他线程主题的更多信息,请参见我的线程教程乔·阿尔巴哈里(Joe Albahari)的人.

For more information on this and other threading topics, see my threading tutorial or Joe Albahari's one.

这篇关于如何从另一个线程在GUI上更新文本框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆