Приложение C # Windows Forms — обновление графического интерфейса из другого потока и класса?

#c# #.net #winforms #multithreading

#c# #.net #winforms #многопоточность

Вопрос:

Я много искал, но, похоже, не могу найти ничего, относящегося к моей конкретной проблеме.

Я хочу иметь возможность обновлять мою форму MainUI из другого класса (SocketListener), и внутри нее у меня есть поток, который обрабатывает сеть (clientThread). Прямо сейчас я могу запускать простые выходные данные из сетевого потока, такие как запись в выходные данные отладчика и создание окна сообщения.

Но что я действительно хочу сделать, так это иметь возможность вызывать код из clientThread, который будет выполнять действия в моем экземпляре MainUI. Как я могу это сделать?

Кроме того, если кому-то нужны конкретные фрагменты кода, я могу опубликовать это, чтобы помочь вам лучше понять, о чем я спрашиваю.

С наилучшими пожеланиями!

Ответ №1:

Проверьте InvokeRequired класс Control , и если это true, затем вызовите Invoke и передайте делегат (обычно анонимный метод), который выполняет то, что вы хотите сделать в потоке клиента.

Пример:

 public void DoWork(Form form)
{
    if (form.InvokeRequired)
    {
        // We're on a thread other than the GUI thread
        form.Invoke(new MethodInvoker(() => DoWork(form)));
        return;
    }

    // Do what you need to do to the form here
    form.Text = "Foo";
}
  

Комментарии:

1. Поскольку я нахожусь в классе, отличном от пользовательского интерфейса, как мне получить переменную «form», которая указывает на текущую запущенную форму, чтобы передать ее в DoWork?

2. Не подойдет ли какой-нибудь синтаксический сахар, подобный AutoProperty, для такого рода шаблонов?

3. @firstEncounter: вы могли бы добавить в свой класс конструктор, который принимает Form в качестве параметра, затем сохранить ссылку в переменной уровня класса.

4. Вам потребуется изменить видимость элементов управления, которые вы хотите изменить, на общедоступные или внутренние. Либо это, либо создайте в своем классе form метод, который изменяет элемент управления, который вы хотите изменить, и вызовите его. В любом случае вам придется перейти к вашему производному типу формы в вашем конструкторе (или откуда бы вы ни взяли экземпляр формы).

5. @firstEncounter, что вы сделали, чтобы изменить атрибуты формы? У меня возникли некоторые проблемы с этим. Я работаю над очень похожей программой

Ответ №2:

Да, вы могли бы добавить в свой класс конструктор, который принимает форму MainUI в качестве параметра. Это быстрый способ сделать это, но он вводит «обратную» зависимость от вашего класса к MainUI, где, теоретически, никакой зависимости не требуется.

В качестве альтернативы я бы предложил добавить общедоступное событие в ваш класс, на которое затем могла бы подписаться форма MainUI. Когда вашему классу необходимо обновить MainUI (или элементы управления в MainUI), класс просто «вызовет» событие. При этом будет вызван метод MainUI, который он зарегистрировал во время подписки. Поскольку это уже метод формы MainUI, все, что вам нужно сделать, это обновить соответствующие элементы управления в форме, убедившись, что учитывается InvokeRequired свойство каждого элемента управления.

И при этом вот конструкция, которую я использую во всем своем коде.

 class NetworkEventArgs : EventArgs { /* defined class here */ }
private void NetworkEventHandler(object sender, NetworkEventArgs e)
{ 
    Invoke( ( MethodInvoker ) delegate {
        myTextBox.Text = e.Message;
    }
}
  

Я основал это на записи в блоге здесь. Этот подход меня еще не подводил, поэтому я не вижу причин усложнять свой код рекурсивной проверкой InvokeRequired свойства.

Комментарии:

1. Этот способ структурирования моего кода действительно хорошо сработал для меня. Я просто хочу добавить, что у меня возникли проблемы с Invoke / MethodInvoker, мой графический интерфейс просто зависал. Я изменил его на BeginInvoke(новый MyDelegate(MyDelegateMethod), myParam); Теперь отлично работает.

Ответ №3:

вы можете определить событие для вашего класса clientThread

и обработайте это в mainform когда clientThread нуждается в mainform для выполнения чего-либо (например, обновления некоторого статуса элемента управления), вы должны запустить событие

итак, mainform получает параметр из события и вызывает функцию обновления