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

查看:30
本文介绍了如何从另一个线程更新 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... 
";  
}

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

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);

推荐答案

您需要使用 BackgroundWorkerControl.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关于参数传递的文章了解更多详情.
  • 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天全站免登陆