Можно ли использовать 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, я не могу этого сделать, так как получаю ошибку «операция с несколькими потоками не действительна».
Кто-нибудь поможет?
В дополнение к некоторым из полученных ответов я поясню «почему». Мне нужно загрузить / обновить элемент управления в моем графическом интерфейсе без блокировки остальной части приложения. Элемент управления содержит множество элементов управления (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 (очевидно), а как необработанное исключение. Пока я не смогу их поймать, мне придется выполнять всю эту операцию синхронно. Как мне успешно отловить ошибку и загрузить остальные мои элементы управления? Или как мне выполнить эту асинхронную операцию другим способом с надлежащей обработкой исключений?