Странные ошибки в многопоточном интерфейсе - PullRequest
6 голосов
/ 14 октября 2008

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

Модель, которая содержит выбор пользователя, имеет IList<T>, где T - локальный объект, Step, который реализует INotifyPropertyChanged, поэтому в пользовательском интерфейсе он монтируется в DataGridView. Все хорошо во время выполнения, начальное состояние объектов отображается на экране.

Каждый из Step объектов является задачей, которая выполняется по очереди; некоторые свойства будут изменены, отражены обратно в IList и переданы в DataGridView.

Это действие в версиях пользовательского интерфейса выполняется путем создания BackgroundWorker, возвращающего события в пользовательский интерфейс. Step делает это и генерирует объект StepResult, который является перечислимым типом, указывающим результат (например, Running, NotRun, OK, NotOK, Caveat) и строку для обозначения сообщения (потому что шаг выполнен, но не совсем как ожидается, т.е. с оговоркой). Обычно действия включают взаимодействие с базой данных, но в режиме отладки я случайным образом генерирую результат.

Если сообщение пустое, проблем не возникает, но если я генерирую ответ, подобный этому:

StepResult returnvalue = new StepResult(stat, "completed with caveat")

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

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

StepResult returnvalue = new StepResult(stat, r.ToString());

действия выполняются без проблем, числа четко записываются в DataGridView.

Я сбит с толку. Я предполагаю, что это как-то строковая буквальная проблема, но может кто-нибудь придумать более четкое объяснение?

Ответы [ 5 ]

4 голосов
/ 14 октября 2008

Вы ответили на свой вопрос: -

Я получаю сообщение об ошибке, говорящее о том, что к DataGridView обращались из потока, отличного от потока, в котором он был создан.

WinForms настаивает на том, чтобы все действия, выполняемые над формами и элементами управления, выполнялись в контексте потока, в котором была создана форма. Причина этого сложна, но имеет много общего с базовым Win32 API. Подробнее см. Различные записи в блоге The Old New Thing .

Вам нужно использовать методы InvokeRequired и Invoke, чтобы обеспечить постоянный доступ к элементам управления из одного потока (псевдокодиш):

object Form.SomeFunction (args)
{
  if (InvokeRequired)
  {
    return Invoke (new delegate (Form.Somefunction), args);
  }
  else
  {
    return result_of_some_action;
  }
}
3 голосов
/ 14 октября 2008

Поскольку вы выполняете привязку пользовательского интерфейса через подписку на событие, может оказаться полезным ; это пример, который я написал некоторое время назад, который показывает, как создать подкласс BindingList<T>, чтобы уведомления автоматически перенаправлялись в поток пользовательского интерфейса.

Если нет синхронизирующего контекста (то есть режима консоли), то он возвращается к простому прямому вызову, поэтому нет никаких накладных расходов. При работе в потоке пользовательского интерфейса обратите внимание, что он по существу использует Control.Invoke, который просто запускает делегат напрямую, если он находится в потоке пользовательского интерфейса. Таким образом, переключение возможно только в том случае, если данные редактируются из потока, не являющегося пользовательским интерфейсом, - просто то, что нам нужно; -p

0 голосов
0 голосов
/ 14 октября 2008

Я нашел эту статью - " Обновление IBindingList из другого потока " - в которой указывалось на виновный список BindingList -

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

Явно передав родительскую форму как ISynchronizeInvoke объект и создавая оболочку для BindingList<T>, добились цели.

0 голосов
/ 14 октября 2008

У меня была такая же проблема раньше. Может быть, эта статья, которую я опубликовал, может помочь.

http://cyberkruz.vox.com/library/post/net-problem-async-and-windows-forms.html

...