Как обновить текстовое поле в графическом интерфейсе из другого streamа

Я новичок в C #, и я пытаюсь создать простое приложение чата для клиентского сервера.

У меня есть RichTextBox в моей форме окон клиента, и я пытаюсь обновить этот элемент управления с сервера, который находится в другом classе. Когда я пытаюсь это сделать, я получаю сообщение об ошибке: «Работа с кросс-streamами недействительна: Control textBox1 доступен из streamа, отличного от streamа, на котором он был создан».

Здесь код моей 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 listBox1) { //here i am trying to update that control and where i get that exception textBox1.Text += "Connected to server... \n"; } 

Итак, как это сделать? Как я могу обновить элемент управления текстовым полем из другого streamа?


Я пытаюсь создать базовое клиентское / серверное приложение чата, используя удаленную сеть .net. Я хочу, чтобы windows создавали клиентское приложение и консольное серверное приложение как отдельные .exe-файлы. Здесь im пытается вызвать функцию сервера AddUser от клиента, и я хочу, чтобы функция AddUser обновила мой графический интерфейс. Ive модифицированный код, как вы предложили Jon, но теперь вместо исключения с перекрестными streamами у меня есть это исключение … »SerializationException: Type Topic в Assembly не помечен как сериализуемый» .

Я выложил весь мой код ниже, постараюсь максимально упростить его.
Любое предложение приветствуется. Большое спасибо.

Сервер:

  namespace Test { [Serializable] public class Topic : MarshalByRefObject { public bool AddUser(string user, RichTextBox textBox1, List 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); 

Вам нужно либо использовать BackgroundWorker , либо Control . Invoke / BeginInvoke . Анонимные функции – анонимные методы (C # 2.0) или lambda-выражения (C # 3.0) делают это проще, чем раньше.

В вашем случае вы можете изменить свой код на:

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

Несколько замечаний:

  • Чтобы соответствовать соглашениям .NET, это следует назвать AddUser
  • Вам не нужно передавать текстовое поле или список по ссылке. Я подозреваю, что вы не совсем понимаете, что на самом деле означает ref – см. Мою статью о передаче параметров для более подробной информации.
  • Разница между Invoke и BeginInvoke заключается в том, что BeginInvoke не будет ждать, пока делегат будет вызван в streamе пользовательского интерфейса до его продолжения, поэтому AddUser может вернуться до фактического обновления текстового AddUser . Если вы не хотите этого асинхронного поведения, используйте Invoke .
  • Во многих образцах (включая некоторые из моих!) Вы найдете людей, использующих Control.InvokeRequired чтобы узнать, нужно ли им вызывать Invoke / BeginInvoke . В большинстве случаев это на самом деле избыточно – нет никакого реального вреда при вызове Invoke / BeginInvoke даже если вам это не нужно, и часто обработчик будет когда-либо вызываться из streamа, отличного от UI. Опускание проверки делает код более простым.
  • Вы также можете использовать BackgroundWorker как я упоминал ранее; это особенно подходит для индикаторов выполнения и т. д., но в этом случае, вероятно, так же легко сохранить свою текущую модель.

Для получения дополнительной информации об этой и других тематических разделах см. Мой учебник по streamам или один из них Джо Альбахари .

Использовать метод Invoke

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

Теперь создайте делегат, который имеет ту же подпись, что и ранее определенный метод:

 public delegate void UpdateTextCallback(string text); 

В вашем streamе вы можете вызвать метод Invoke на вашем TextBox, передать делегат для вызова, а также параметры.

 yourTextBox.Invoke(new UpdateTextCallback(this.UpdateText), new object[]{”Text generated on non-UI thread.”}); 
  • Android ViewPager с нижними точками
  • Создание пользовательской кнопки в Java с помощью JButton
  • Масштабировать пользовательский интерфейс для нескольких разрешений / различных устройств
  • Картина не клиента на окне аэрозона
  • Как я могу получить высоту и ширину панели навигации Android программно?
  • Как я могу улучшить внешний вид приложения для Android?
  • Обновление пользовательского интерфейса из разных streamов в JavaFX
  • Android: что выбрать для значений кода запроса?
  • JTable Right Align Header
  • JButton ActionListener - обновление GUI только после нажатия JButton
  • Сериализовать компоненты JavaFX
  • Давайте будем гением компьютера.