Дескриптор окна

Рейтинг: 1Ответов: 1Опубликовано: 26.02.2015

Добрый день.

У меня возникла непредвиденная проблема: при вызове контролла компонента формы в потоке, точнее, есть основной поток, который вызывает backgroundworker, тот вызывает новый поток, и тот, в свою очередь, вызывает при необходимости последний поток, содержащий вызовы методов (потоки синхронизированы). В структуре одного из которых (методов) содержится запись:

this.Invoke(new Action(() =>                           
    dataGridView2.Rows[
          Int32.Parse(SendMessage.ValueSendMessage.SendIndexUser[i, 0])
        ].Cells[2].Value = ""));

вызвав которую ловлю exception следящего содержания:

Invoke or BeginInvoke cannot be called on a control until the window handle has been created

Вопрос следующий: форма же одна, и она уже создана, почему он говорит, что не может управлять элементом до завершения её создания?

P.S. Зато приведенный выше вызов dataGridView2 прекрасно работает в backgroundworker.

Ответы

▲ 1

Invoke перебрасывает вызов не просто в какой-то "главный" поток. Он перебрасывает вызов именно в тот поток, в котором крутится цикл отрисовки нативного контола, обернутого в контрол WinForms. Т.е. для корректной работы ему нужен дескриптор (handle) нативного контрола.

Нативный контрол создается не в момент создания (вызова конструктора) формы, а (обычно) в момент ее отображения на экране.

Если ваш поток как-то обгоняет момент появления this (точнее, нативного контрола, который в этот this завернут) - Invoke будет выбрасывать именно эту ошибку. Т.е. нельзя создать форму, заполнить ее в фоновом потоке, а потом уже показать.

Вам нужно или дожидаться появления this (создания его handle) на экране. И откладывать любые Invoke до момента, пока IsHandleCreated станет равным true.

Или

просто использовать проверку на InvokeRequired. Система с Invoke создана для того, чтобы избежать трудноуловимых багов при изменении данных в одном потоке, в момент отрисовки контрола в другом потоке. Нет handle - нет цикла отрисовки - InvokeRequired вернет false - эта проверка + проверка на совпадение потоков - и есть суть его стандартной реализации:

var action = new Action(() =>                           
    dataGridView2.Rows[
          Int32.Parse(SendMessage.ValueSendMessage.SendIndexUser[i, 0])
        ].Cells[2].Value = "");

if (this.InvokeRequired)
{
    this.Invoke(action);
}
else
{
    action();
}