2009-07-16 32 views
9

Tôi mới sử dụng C# và tôi đang cố gắng tạo một ứng dụng trò chuyện máy chủ ứng dụng khách đơn giản.Cách cập nhật hộp văn bản trên GUI từ một chủ đề khác

Tôi có RichTextBox trên biểu mẫu cửa sổ ứng dụng khách và tôi đang cố cập nhật điều khiển đó từ máy chủ nằm trong một lớp khác. Khi tôi cố gắng làm điều đó tôi nhận được lỗi: "Hoạt động chéo luồng không hợp lệ: Kiểm soát textBox1 được truy cập từ một chủ đề khác với chuỗi được tạo trên".

Ở đây mã của hình thức Windows của tôi:

private Topic topic; 
public RichTextBox textbox1; 
bool check = topic.addUser(textBoxNickname.Text, ref textbox1, ref listitems); 

Topic lớp:

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"; 
} 

Vậy làm thế nào để làm điều đó? Làm cách nào để cập nhật điều khiển hộp văn bản từ một chuỗi khác?


Tôi đang cố gắng thực hiện một số ứng dụng khách/máy chủ trò chuyện cơ bản bằng cách sử dụng tính năng truy cập từ xa. Tôi muốn tạo các ứng dụng máy khách và ứng dụng máy chủ dạng cửa sổ dưới dạng các tệp .exe riêng biệt. Ở đây im cố gắng gọi chức năng máy chủ AddUser từ máy khách và tôi muốn chức năng AddUser cập nhật GUI của tôi. Ive sửa đổi mã như bạn đề nghị Jon nhưng bây giờ thay vì ngoại lệ chủ đề tôi đã có ngoại lệ này ... "SerializationException: Loại chủ đề trong hội không được đánh dấu là serializable".

Đánh dấu toàn bộ mã của tôi dưới đây, sẽ cố gắng giữ cho nó đơn giản nhất có thể.
Bất kỳ đề xuất nào đều được hoan nghênh. Cảm ơn nhiều.

Server:

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

} 

của Windows hình thức khách hàng:

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

Trả lời

32

Bạn cần phải sử dụng một trong hai BackgroundWorker, hoặc Control. Invoke/BeginInvoke. Các hàm ẩn danh - các phương thức ẩn danh (C# 2.0) hoặc các biểu thức lambda (C# 3.0) làm cho việc này dễ dàng hơn trước đây.

Trong trường hợp của bạn, bạn có thể thay đổi mã của bạn để:

public bool AddUser(string user, RichTextBox textBox1, List listBox1) 
{ 
    MethodInvoker action = delegate 
     { textBox1.Text += "Connected to server... \n"; }; 
    textBox1.BeginInvoke(action); 
} 

Một số điều cần lưu ý:

  • Để phù hợp với công ước NET, điều này nên được gọi là AddUser
  • Bạn không cần phải chuyển hộp văn bản hoặc hộp danh sách theo tham chiếu. Tôi nghi ngờ bạn không hoàn toàn hiểu những gì ref thực sự có nghĩa là - xem my article on parameter passing để biết thêm chi tiết.
  • Sự khác biệt giữa InvokeBeginInvokeBeginInvoke sẽ không chờ đợi cho các đại biểu được gọi là trên thread UI trước khi nó tiếp tục - vì vậy AddUser có thể trở lại trước khi textbox có thực được cập nhật. Nếu bạn không muốn hành vi không đồng bộ đó, hãy sử dụng Invoke.
  • Trong nhiều mẫu (bao gồm một số mẫu của tôi!), bạn sẽ tìm thấy những người đang sử dụng Control.InvokeRequired để xem liệu họ có cần gọi số Invoke/BeginInvoke hay không. Đây thực sự là quá mức cần thiết trong hầu hết các trường hợp - không có tổn hại thực sự khi gọi Invoke/BeginInvoke ngay cả khi bạn không cần và thường thì trình xử lý chỉ được gọi là từ một chuỗi không phải giao diện người dùng. Bỏ qua kiểm tra làm cho mã đơn giản hơn.
  • Bạn cũng có thể sử dụng BackgroundWorker như tôi đã đề cập trước đây; điều này đặc biệt phù hợp với các thanh tiến trình, nhưng trong trường hợp này, có thể dễ dàng giữ mô hình hiện tại của bạn.

Để biết thêm thông tin về chủ đề này và luồng khác, hãy xem my threading tutorial hoặc Joe Albahari's one.

10

Sử dụng Gọi phương pháp

// Updates the textbox text. 
private void UpdateText(string text) 
{ 
    // Set the textbox text. 
    yourTextBox.Text = text; 
} 

Bây giờ, tạo ra một đại biểu có chữ ký giống như phương pháp đã được xác định trước đó:

public delegate void UpdateTextCallback(string text); 

Trong chủ đề của bạn, bạn có thể gọi phương thức Invoke trên yourTextBox, chuyển giao ủy quyền để gọi, cũng như các tham số.

yourTextBox.Invoke(new UpdateTextCallback(this.UpdateText), 
      new object[]{”Text generated on non-UI thread.”}); 
Các vấn đề liên quan