2012-01-26 16 views
10

既存のプロジェクトで作業していますが、私はWinFormsを使用しなければなりません(しばらくの間使っていない)UIスレッドと同期する問題があります。UIスレッドとワーカーを同期させる

私が統合しなければならないデザインは、次のようになります。は、Actionをパラメータとして取得し、非同期に実行します。私が取り組んでいるアクションには2つの部分があります。コア・クラス(ビジネス・ロジックを含む)と、ユーザーの対話を要求しなければならない場合、イベントによってコアによって通知されるGUI部分。

私はこれで

if (!IsHandleCreated) 
{ 
    //be sure to create the handle in the constructor 
    //to allow synchronization with th GUI thread 
    //when using Show() or ShowDialog() 
    CreateHandle(); 
} 

フォームのコンストラクタにハンドルの作成を追加した、コードは以下を動作します。

private DialogResult ShowDialog(Form form) 
{ 
    DialogResult dialogResult = DialogResult.None; 
    Action action = delegate { dialogResult = form.ShowDialog(); }; 
    form.Invoke(action); 
    return dialogResult; 
} 

この例では、起動場所は、Windowsに設定されていますデフォルト。

ParentWindowIWin32Windowのインスタンスであると WindowStartupLocationCenterParentに設定されている
Action action = delegate { dialogResult = form.ShowDialog(ParentWindow); }; 

:私はそれを変更した場合

form.Invoke(action)を呼び出すと、クロススレッド例外が発生します。

クロススレッド操作が有効でない:コントロール 'ActivationConfirmationForm'が、作成されたスレッド以外のスレッドからアクセスされました。

質問:

  • CenterParentとして起動場所を設定するときに、なぜクロススレッドの例外しかないのですか?そして、どうすればそれを避けることができますか?
  • はなぜ常に falseのですか?

どちらもおそらく関連しています!

[編集] @Reniuz: あなたがここに何が不足していない;) コールは私の処分で

private static void OnActivationConfirmationRequired(DmsPackageConfiguratorCore sender, 
ConfigurationActivationConfirmationEventArgs args) 
{ 
    args.DoAbort = (ShowDialog(new ActivationConfirmationForm(args.Data)) == DialogResult.No); 
} 

すべてがGUIであるコアによって通知されたリスナーから作られていますインターフェイス

/// <summary> 
/// Interface defining methods and properties used to show dialogs while performing package specific operations 
/// </summary> 
public interface IPackageConfiguratorGui 
{ 
/// <summary> 
/// Gets or sets the package configurator core. 
/// </summary> 
/// <value>The package configurator core.</value> 
IPackageConfiguratorCore PackageConfiguratorCore { get; set; } 

/// <summary> 
/// Gets or sets the parent window. 
/// </summary> 
/// <value>The parent window.</value> 
IWin32Window ParentWindow { get; set; } 

/// <summary> 
/// Gets the package identifier. 
/// </summary> 
/// <value>The package identifier.</value> 
PackageIdentifier PackageIdentifier { get; } 
} 
+0

は、「CenterParent」のために、または実際に親ウィンドウを設定しているため(つまり、 'CenterParent'設定を削除しても起動します)、クロススレッド例外が発生します。 – Strillo

+0

@ Strillo ParentWindow – Philippe

+0

フォームを呼び出して自分自身を表示するように設定すると、常に起動しますか?またはIamはここに何かを逃している?すべてのコードを投稿できますか? – Reniuz

答えて

3

見てform.InvokeRequiredではあなたの問題の中核です。あなたはそれが真実でなければならないことを知っている。簡単な説明は、ShowDialog()メソッドに渡されるフォームオブジェクトが間違ったオブジェクトであることです。古典的な間違いは、フォームオブジェクトの既存のインスタンスを使用する代わりに新しいを使用してインスタンスを作成することです。これは、ユーザーが見ていてメインスレッドで作成されたものです。正しい参照を渡すことができるように、スレッドコードにそのフォームオブジェクトへの参照があることを確認してください。正しく取得できない場合は、Application.OpenForms [0]を使用してください。

一般に、スレッドコードをユーザーインターフェイスから切り離します。ワーカースレッドにはダイアログが表示されません。あなたは動作させることはできますが、実際にはうまく動作しません。ダイアログは、ユーザーがそれを期待することなくポップアップします。事故を起こす可能性があるため、ユーザは、ダイアログがポップアップする前に数秒で何かをクリックしたり、キーを押したりするかもしれない。ダイアログも表示せずに終了します。同様に、CreateHandle()ハックはでなく、でなければなりません。ユーザーインターフェイスが準備できるまでスレッドを開始しないでください。フォームのLoadイベントによって通知されます。

+0

説明してくれてありがとう!このフォームは実際には、GUIスレッドではなく、作業者のGUI部分でオンザフライで作成されます。これは私が変更できない設計上の問題です。ユーザーはWindowsのデフォルトの場所でダイアログがポップアップして生きなければなりません;) – Philippe

1

ok私は新しいユーザーであるため「コメントする」権限がありません。この回答スペースを使用します。

このフォームの作成とShowDialogの実行が同じスレッドコンテキストで実行されている場合は、ActivationConfirmationFormというフォームの新しいインスタンスを作成しています。これはTrueであるため、InvokeRequired(msdnを参照)あなたがアクセスしたいフォームがアクセスしているスレッドで作成されているので、明らかにfalseになります。 @ reniuzが心配していたものの種類など、invoke/begininvokeなどを使う必要はありません。

1

フォームは、親とは異なるスレッドに属します。

WinForms CenterParentの位置引数は、Win32 APIを使用してHWNDから親ウィンドウ位置を見つけるのではなく、WinForms .Netオブジェクトを呼び出すように見えます。このクロススレッド呼び出しは、クロススレッド例外を引き起こします。

本当の答えは、ワーカースレッドはUIを持つべきではありません。彼らは、ユーザーの介入が必要であり、メインスレッドがユーザーのやりとりを処理する必要があることを示す結果を与える必要があります。

これに失敗した場合は、ワーカースレッドのGUIに親ウィンドウを設定しないでください。ワーカースレッドが1つしかない場合は実行可能になる可能性がありますが、それ以上のスレッドがない場合はあらゆる混乱を招きます。

絶対に必要な場合は、P/Invokeを使用して、親ウィンドウの現在のウィンドウ位置をWin32APIから探し、明示的に設定します。

0

フォローアップ:
私は今、実用的な解決策を持っています(ダイアログは親を設定せずにモーダルではありません)。ワーカースレッドから対話を開始するのが一番いいことではないことに同意しますが、この場合は要件です。

private void OnActivationConfirmationRequired(DmsPackageConfiguratorCore sender, ConfigurationActivationConfirmationEventArgs args) 
{ 
    //create the dialog in the graphical thread 
    ActivationConfirmationForm dialog = null; 
    Action createDialogInGuiThread =() => dialog = new ActivationConfirmationForm(args.Data); 
    ParentForm.Invoke(createDialogInGuiThread); 

    if (dialog != null) 
    { 
     args.DoAbort = (ShowDialog(dialog) == DialogResult.No); 
    } 
} 

は、すべてのグラフィカルなものとしては、UIスレッド

private DialogResult ShowDialog(Form form) 
{ 
    DialogResult dialogResult = DialogResult.None; 

    //launch the form in the graphical htread (the one of the parent form) 
    Action action = delegate { dialogResult = form.ShowDialog(ParentForm); }; 
    ParentForm.Invoke(action); 

    return dialogResult; 
} 

から対話を起動します。グラフィカルなスレッドでの対話を作成します

前IWin32Windowとは対照的に、ParentFormは、今のフォームですUIスレッドで処理が行われている場合、ハンドルの作成はもう必要ありません。