UI Контейнер в потоке? - PullRequest
       24

UI Контейнер в потоке?

2 голосов
/ 18 октября 2011

братьев по коду.

Я пытаюсь сделать мое приложение WinForms многопоточным. В DoWork моего фонового работника у меня есть метод, который меняет несколько элементов управления с помощью делегата MethodInvoker. У меня вопрос: должен ли я вызывать каждый элемент управления каждый раз, когда я хочу изменить его из другого потока, или, может быть, существует какой-то контейнер элементов управления, который я могу вызвать, чтобы избежать множественного вызова определенных элементов управления?

Ответы [ 5 ]

3 голосов
/ 18 октября 2011

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

Если вы хотите «скрыть» вызовы, вам нужно написать класс, который при запуске будет обнаруживать изменения в его свойствах и использовать Invoke в коде, который взаимодействует с вашими элементами управления способом, зависящим от этих свойств. , Таким образом, рабочий процесс будет:

  1. Ваш работник изменяет свойства "контроллера", не вызывая. Это не имеет немедленного эффекта.
  2. В какой-то момент контроллер «запускается» (возможно, периодически работником?).
  3. Контроллер обнаруживает (или уже знает), какие изменения были внесены в его свойства и как они преобразуются в вызов кода для элементов управления. Он вызывает блок кода, который соответствующим образом взаимодействует с элементами управления.
0 голосов
/ 18 октября 2011
public void UpdateControl<T>(T control, Action<T> action) where T : Control
{
  if(control.InvokeRequired) 
    control.Invoke(() => action(control));
  else
    action(control);
}
0 голосов
/ 18 октября 2011

Допустим, вы хотите изменить текст на два Label с.Предполагая, что они принадлежат одному и тому же Form, вы можете сделать это либо по отдельным вызовам Invoke ...

void buttonInvoke_Click(object sender, EventArgs e) {
    Invoke((Action)(() => label1.Text = "A1"));
    Invoke((Action)(() => label2.Text = "A2"));
}

..., либо сгруппировав затем в один Invoke, чтобы сохранитьнекоторые набирают текст и увеличивают производительность.

private void buttonInvoke_Click(object sender, EventArgs e) {
    Invoke(
        (Action)(() => {
            label1.Text = "B1";
            label2.Text = "B2";
        })
    );
}
0 голосов
/ 18 октября 2011

Технически весь ваш графический интерфейс создается в главном потоке, поэтому, если вы вызываете, скажем, главную панель в графическом интерфейсе, в этом методе вызова вы можете изменить другие элементы управления в графическом интерфейсе, все в этом методе

Plusесли ваш фоновый работник был создан в главном потоке, то вы можете вызвать событие прогресса отчета обратно в основной поток ... Это означает, что вызов не требуется.Основная цель фоновых работников действительно.

0 голосов
/ 18 октября 2011

Мой вопрос заключается в том, должен ли я вызывать каждый элемент управления каждый раз, когда я хочу изменить его из другого потока, или, может быть, существует какой-то контейнер элементов управления, который я могу вызвать, чтобы избежать многократного вызова определенных элементов управления?

Вы должны вызывать каждый раз, когда хотите изменить пользовательский интерфейс.Операция invoke может сделать больше, чем просто изменить один peroeprty - это может быть полная функция, которая заменяет 100 элементов управления.

Минимизация вызовов - это производительность.

Нет, заранее заданный контейнер отсутствуетПредполагается, что вы умеете самостоятельно вызывать программу, например, для блока анонимного кода.

...