Control.BeginInvoke を「ファイア アンド フォーゲット」方式以外で使用することは可能ですか? 次のリクエストを変更してコールバック メソッドをデリゲートし、各非同期呼び出しが完了したときに何かを実行できるようにします。
this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[] { ctrl, ctrl.DsRules, ctrl.CptyId });
通常のデリゲートでこれを行うことができます.BeginInvokeなど
RefreshRulesDelegate del = new RefreshRulesDelegate(RefreshRules);
del.BeginInvoke(ctrl, ctrl.DsRules, ctrl.CptyId, new AsyncCallback(RefreshCompleted), del);
しかし、Control .BeginInvoke を呼び出しているため、「クロススレッド操作が無効です」というエラーが発生するため、これを行うことはできません。
誰か助けて?
受け取った回答のいくつかに加えて、「なぜ」を明確にします。アプリの残りの部分をロックせずに、GUI でコントロールをロード/更新する必要があります。コントロールには、データセットを取得して渡す必要がある多数のコントロール (ruleListCtls) が含まれています。すなわち
public void RefreshAll()
{
foreach (LTRFundingRuleListControl ctrl in ruleListCtls)
{
this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[]{ctrl,ctrl.DsRules, ctrl.CptyId });
}
}
デリゲート コールバック メソッドを提供し、コントロールを修正するコードをそれらが作成されたメインの GUI スレッドに戻すと、これを実行できることがわかりました (クロススレッド エラーを回避するため)。
public void RefreshAll()
{
IntPtr handle;
foreach (LTRFundingRuleListControl ctrl in ruleListCtls)
{
handle = ctrl.Handle;
RefreshRulesDsDelegate del = new RefreshRulesDsDelegate(RefreshRulesDs);
del.BeginInvoke(ctrl.DsRules, ctrl.CptyId, handle, out handle, new AsyncCallback(RefreshCompleted), del);
}
}
private void RefreshCompleted(IAsyncResult result)
{
CptyCatRuleDataSet dsRules;
string cptyId;
IntPtr handle;
AsyncResult res = (AsyncResult) result;
// Get the handle of the control to update, and the dataset to update it with
RefreshRulesDsDelegate del = (RefreshRulesDsDelegate) res.AsyncDelegate;
dsRules = del.EndInvoke(out handle,res);
// Update the control on the thread it was created on
this.BeginInvoke(new UpdateControlDatasetDelegate(UpdateControlDataset), new object[] {dsRules, handle});
}
public delegate CptyCatRuleDataSet RefreshRulesDsDelegate(CptyCatRuleDataSet dsRules, string cptyId, IntPtr ctrlId, out IntPtr handle);
private CptyCatRuleDataSet RefreshRulesDs(CptyCatRuleDataSet dsRules, string ruleCptyId, IntPtr ctrlId, out IntPtr handle)
{
try
{
handle = ctrlId;
int catId = ((CptyCatRuleDataSet.PSLTR_RULE_CAT_CPTY_SelRow)dsRules.PSLTR_RULE_CAT_CPTY_Sel.Rows[0]).RULE_CAT_ID;
return ltrCptyRulesService.GetCptyRules(ruleCptyId, catId);
}
catch (Exception ex)
{
throw ex;
}
}
コールバックを受信したメイン スレッドに委譲する内容は次のとおりです。
private delegate void UpdateControlDatasetDelegate(CptyCatRuleDataSet dsRules, IntPtr ctrlId);
private void UpdateControlDataset(CptyCatRuleDataSet dsRules, IntPtr ctrlId)
{
IEnumerator en = ruleListCtls.GetEnumerator();
while (en.MoveNext())
{
LTRFundingRuleListControl ctrl = en.Current as LTRFundingRuleListControl;
if (ctrl.Handle == ctrlId)
{
ctrl.DsRules = dsRules;
}
}
}
これで問題なく動作します。ただし、これが特にエレガントだとは思わないことを除けば、主な問題は例外処理です。これは別の質問かもしれませんが、RefreshRulesDs が例外をスローすると、エラーが (明らかに) GUI スレッドにバブルアップされず、未処理の例外として発生するため、アプリがクラッシュします。これらをキャッチできるまで、この操作全体を同期的に行う必要があります。エラーをキャッチして残りのコントロールを読み込むにはどうすればよいですか? または、適切な例外処理を使用して、この非同期操作を別の方法で実現するにはどうすればよいですか?