Invoke или BeginInvoke нельзя вызвать в элементе управления до тех пор, пока дескриптор окна не будет создан

Я получаю следующее исключение:

Invoke или BeginInvoke нельзя вызвать в элементе управления до тех пор, пока не будет создан дескриптор окна.

Это мой код:

if (InvokeRequired)
{
    BeginInvoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
}
else
    Invoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);

Я нашел страницы об этой теме на этом сайте, но я не знаю, что не так.

Ответ 1

Разница между Invoke и BeginInvoke заключается в том, что первая является синхронной (ждет завершения), а позже - асинхронной (вроде fire-and-forget). Тем не менее, обе работают, отправляя сообщение в цикл сообщений пользовательского интерфейса, что приведет к тому, что делегат будет выполнен, когда он дойдет до этого сообщения.

Свойство InvokeRequired определяет, нужно ли вообще вызывать Invoke или если он уже находится в правильном потоке, а не хотите ли вы синхронного или асинхронного вызова. Если InvokeRequired является ложным, вы (теоретически) уже работаете в потоке пользовательского интерфейса и можете просто выполнять синхронные действия напрямую (или все же BeginInvoke, если вам нужно асинхронно запускать их). Это также означает, что вы не можете использовать Invoke, если InvokeRequired имеет значение false, потому что нет пути для продолжения цикла сообщений в текущем потоке. Так что одна большая проблема с вашим кодом выше, но не обязательно ошибка, о которой вы сообщаете. Фактически вы можете использовать BeginInvoke в любом случае, если вы следите за рекурсивным вызовом и так далее.

Однако вы не можете использовать ни один из них без дескриптора окна. Если Form/Control был создан, но не инициализирован (т.е. До его первого показа), он может еще не иметь дескриптора. И ручка очищается Dispose(), например, после закрытия формы. В любом случае InvokeRequired вернет false, потому что невозможно вызвать без дескриптора. Вы можете проверить IsDisposed, и есть также свойство IsHandleCreated, которое более конкретно проверяет, существует ли дескриптор. Обычно, если IsDisposed является истинным (или IsSandleCreated является ложным), вы хотите использовать инструмент для особого случая, например, просто отказаться от действия как неприменимого.

Таким образом, код, который вы хотите, скорее всего, похож:

if (IsHandleCreated)
{
    // Always asynchronous, even on the UI thread already.  (Don't let it loop back here!)
    BeginInvoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
    return; // Fired-off asynchronously; let the current thread continue.

    // WriteToForm will be called on the UI thread at some point in the near future.
}
else
{
    // Handle the error case, or do nothing.
}

Или, может быть:

if (IsHandleCreated)
{
    // Always synchronous.  (But you must watch out for cross-threading deadlocks!)
    if (InvokeRequired)
        Invoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
    else
        WriteToForm(finished, numCount); // Call the method (or delegate) directly.

    // Execution continues from here only once WriteToForm has completed and returned.
}
else
{
    // Handle the error case, or do nothing.
}

Ответ 2

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

Сообщение также может появиться после размещения Формы.

Вы можете проверить IsHandleCreated чтобы узнать, была ли уже создана форма, но вам нужно поместить все в правильную обработку ошибок, поскольку оператор Invoke может выдать исключение, если вы попытаетесь обновить форму во время закрытия приложения.

Ответ 3

вот мой ответ

Скажем, вы хотите написать "Hello World" в текстовое поле. Затем, если вы используете "Ishandlecreated", тогда ваша операция не произойдет, если обработчики еще не созданы. Поэтому вы должны заставить себя CreateHandlers, если он еще не создан.

Вот мой код

if (!IsHandleCreated)
    this.CreateControl();

this.Invoke((MethodInvoker)delegate
{
  cmbEmail.Text = null;

});

Ответ 4

Если вы собираетесь использовать элемент управления из другого потока, прежде чем показывать элемент управления или выполнять другие действия с помощью элемента управления, подумайте о том, чтобы заставить его создать дескриптор внутри конструктора. Это делается с помощью функции CreateHandle. В многопоточном проекте, где логика "контроллера" не находится в WinForm, эта функция помогает избежать таких ошибок.

Ответ 5

Предполагая, что форма не расположена, но еще не полностью инициализирована, просто поместите var X = this.Handle; перед этим, if утверждение... this подразумевается экземпляр соответствующей формы.

см. http://msdn.microsoft.com/en-us/library/system.windows.forms.control.handle.aspx.

Ответ 6

Вероятно, вы вызываете это в конструкторе формы, в этот момент дескриптор основного системного окна еще не существует.