GridViewといくつかのユーティリティボタンを保持するタイプのコントロールがあります。コントロールは、私のアプリケーションのどこでも使用されます。デリゲートを介して、非同期で入力されます。
protected virtual void PopulateGridView()
{
if (isPopulating) return;
//a delegate given to the control by its parent form
if (GetterMethod != null)
{
isPopulating = true;
/*unimportant UI fluff here*/
//some controls are fast enough to not have to mess with threading
if(PopulateSynchronously)
{
PopulateWithGetterMethod();
InitializeGridView();
}
else //most aren't
{
Action asyncMethod = PopulateWithGetterMethod;
asyncMethod.BeginInvoke(
ar => Invoke((MethodInvoker)InitializeGridView)), null);
}
}
}
private void PopulateWithGetterMethod()
{
//a list of whetever the control is displaying;
//the control ancestor and this collection are generic.
RetrievedInformation = GetterMethod();
}
protected virtual void InitializeGridView()
{
//use RetrievedInformation to repopulate the GridView;
//implementation not important, except it touches UI elements,
//so it needs to be called from the worker thread using Invoke.
}
実行時間の長いクエリでは、ユーザーが焦ってウィンドウを閉じることがありました。または、コントロールの1つがタイマーに基づいて自動更新されている場合、ユーザーは偶然にウィンドウを閉じます。それが発生し、クエリDIDが終了すると、コントロールにウィンドウハンドルがなかったため、コールバックデリゲートのInvoke呼び出しはInvalidOperationExceptionで失敗しました。
これを修正するために、組み込みのIsHandleCreatedプロパティを使用しようとしました。
...
else
{
Action asyncMethod = PopulateWithGetterMethod;
asyncMethod.BeginInvoke(
ar => { if(IsHandleCreated)
Invoke((MethodInvoker)InitializeGridView));
}, null);
}
ただし、例外はまだ発生しますが、それほど頻繁ではありません。私はそれをなんとか再現し、IsHandleCreatedのウォッチがfalseを示していても、Invoke呼び出しがまだ発生していることを発見しました。私の推測では、スレッドは、イベントデリゲートを発生させる前にnullをチェックする場合に見られるように、チェックとInvoke呼び出しの間に横取りされたと思います。
私にはまだ選択肢があると思いますが、何が最善か疑問に思っています。
- IsHandleCreatedだけでなく、Disposedもチェックして、コントロールが実際に正常に機能していることを確認し、破壊されようとしているだけではないことを確認します。
- チェックを行う前にThread.Yield()を実行して、OSがハンドルをチェックする前にウィンドウ管理を実行できるようにします。
- Invoke呼び出しを、InvalidOperationExceptions、またはウィンドウハンドルの欠如を報告する少なくとも1つを抑制するtry/catchでラップします。正直なところ、この場合、GridViewを更新できないことは気にしません。ユーザーがウィンドウを閉じたので、明らかに彼らは気にしません。アプリ全体を停止することなく、スレッドを静かに終了させます。
3番目のオプションはコップアウトのようです。それを処理するためのよりクリーンな方法が必要です。しかし、他の2つのいずれかが100%修正されるかどうかはわかりません。
編集:DisposedとIsDisposedのチェックも機能しませんでした。条件が「IsHandleCreated&&!Disposed &&!IsDisposed」のifブロック内から例外がスローされました。この例外では、監視時に最初と最後のノードがfalseでした。現在、「ウィンドウハンドルが作成されるまで、コントロールでInvokeまたはBeginInvokeを呼び出すことはできません。」というメッセージを表示して、すべての例外をトラップしています。これは、実行したくないことです。