2009-11-27 27 views
5

Có thể sử dụng Control.BeginInvoke trong bất kỳ mục đích nào khác ngoài "lửa & quên" cách thức không? Tôi muốn thay đổi yêu cầu sau đây để ủy quyền một phương thức gọi lại để tôi có thể thực hiện điều gì đó khi mỗi cuộc gọi không đồng bộ của tôi hoàn tất.Làm cách nào để ủy quyền một phương thức AsyncCallback cho Control.BeginInvoke? (.NET)

this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[] { ctrl, ctrl.DsRules, ctrl.CptyId }); 

Tôi có thể làm điều này với đại biểu bình thường.BeginInvoke ví dụ:

RefreshRulesDelegate del = new RefreshRulesDelegate(RefreshRules); 
      del.BeginInvoke(ctrl, ctrl.DsRules, ctrl.CptyId, new AsyncCallback(RefreshCompleted), del); 

Nhưng vì tôi gọi kiểm soát .BeginInvoke tôi không thể làm điều này khi tôi nhận được những "hoạt động cross-đề không hợp lệ" lỗi.
Bất kỳ ai cũng có thể trợ giúp?

Ngoài một số câu trả lời nhận được, tôi sẽ làm rõ "lý do". Tôi cần phải tải/làm mới một điều khiển trên GUI của tôi mà không cần khóa phần còn lại của ứng dụng. Điều khiển chứa nhiều điều khiển (ruleListCtls) mà tất cả đều yêu cầu một tập dữ liệu được lấy ra và truyền cho chúng. tức là

public void RefreshAll() 
{ 
    foreach (LTRFundingRuleListControl ctrl in ruleListCtls) 
    { 
     this.BeginInvoke(new RefreshRulesDelegate(RefreshRules), new object[]{ctrl,ctrl.DsRules, ctrl.CptyId }); 
    } 
} 

Tôi đã phát hiện ra rằng tôi có thể làm điều này nếu tôi cung cấp một phương pháp đại biểu gọi lại và di chuyển bất kỳ mã đó sửa đổi các điều khiển trở lại vào thread GUI chính mà chúng được tạo ra (để tránh các lỗi cross-chủ đề)

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; 
    } 
} 

Dưới đây là những gì chúng ta delgate đến các chủ đề chính sau khi nhận được gọi lại:

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; 
     } 
    } 
} 

này hiện đang làm việc tốt. Tuy nhiên, vấn đề chính, ngoài ra tôi không nghĩ rằng điều này đặc biệt thanh lịch, là xử lý ngoại lệ. Có lẽ đây là một câu hỏi khác, nhưng nếu RefreshRulesDs ném một ngoại lệ sau đó ứng dụng của tôi bị treo khi lỗi không sôi nổi sao lưu luồng GUI (hiển nhiên) nhưng là một ngoại lệ chưa được xử lý. Cho đến khi tôi có thể bắt được chúng thì tôi sẽ phải thực hiện toàn bộ thao tác này một cách đồng bộ. Làm cách nào để tôi gặp lỗi và tải phần còn lại của các điều khiển của tôi? Hoặc làm cách nào để thực hiện thao tác không đồng bộ này theo cách khác, với xử lý ngoại lệ phù hợp?

+0

Xử lý ngoại lệ hơi lộn xộn, nhưng bạn sẽ có thể bắt chúng bằng EndInvoke xung quanh. BackgroundWorker và Task (Fx 4) làm nó đẹp hơn mặc dù. –

Trả lời

4

Về "Có thể" phần: Không, Control.BeginInvoke sử dụng Windows 'PostMessage() và điều đó có nghĩa là không có câu trả lời. Nó cũng có nghĩa là RefreshRulesDelegate được thực hiện trên luồng chính, chứ không phải trên một chuỗi nền.

Vì vậy, hãy sử dụng delegate.BeginInvoke hoặc ThreadPool và khi chúng được hoàn thành, hãy sử dụng Control.[Begin]Invoke() để cập nhật giao diện người dùng.

+0

Henk, trợ giúp của bạn được đánh giá cao. Tôi đã sửa đổi mã của tôi như bạn đã đề xuất. Xem ở trên. Câu hỏi (triệu đô la) bây giờ là làm thế nào để tôi xử lý bất kỳ trường hợp ngoại lệ ném? – Sheed

0

Vì vậy, bạn muốn "điều phụ" xảy ra trên chuỗi công nhân? (nếu không, bạn chỉ cần chạy nó trong phương thức RefreshRules). Có lẽ chỉ cần sử dụng ThreadPool.QueueUserItem:

ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ }); 

vào cuối (hoặc sau) phương pháp RefreshRules của bạn?

Đối với thông tin, bạn có thể tìm thấy nó dễ dàng hơn/ngăn nắp để gọi BeginInvoke với một phương pháp mang tính chất quá:

this.BeginInvoke((MethodInvoker) delegate { 
    RefreshRules(ctrl, ctrl.DsRules, ctrl.CptyId); 
    ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ }); 
}); 

này tránh việc tạo ra một loại đại biểu, và cung cấp gõ kiểm tra theo yêu cầu của bạn để RefreshRules - lưu ý rằng nó chụp ctrl, mặc dù - vì vậy nếu bạn đang ở trong một vòng lặp bạn sẽ cần một bản sao:

var tmp = ctrl; 
this.BeginInvoke((MethodInvoker) delegate { 
    RefreshRules(tmp, tmp.DsRules, tmp.CptyId); 
    ThreadPool.QueueUserWorkItem(delegate { /* your extra stuff */ }); 
}); 
+0

Cảm ơn. Không chắc chắn rằng giúp mặc dù. Tôi chỉ muốn RefreshCompleted được gọi khi RefreshRules đã kết thúc. Tôi nghĩ rằng sẽ đơn giản – Sheed

+0

nó sẽ dễ dàng hơn để giải thích, nếu bạn giải thích một chút nguyên nhân của câu hỏi. Vấn đề gì đã gây ra câu hỏi của bạn. Có lẽ có một cách dễ dàng hơn để làm điều đó. – serhio

2

bạn có thể làm điều này:

this.BeginInvoke(delegate 
{ 
    RefreshRules(ctrl, ctrl.DsRules, ctrl.CptyId); 
    RefreshCompleted(); 
}); 

EDIT:

tôi sẽ xem xét loại bỏ các lập luận IAsyncResult từ phương pháp RefreshCompleted và sử dụng các giải pháp trên.

Nếu vì một lý do nào đó bạn thực sự cần phải giữ đối số IAsyncResult. Bạn có thể thực hiện một phương pháp mở rộng cho điều khiển:

public static IAsyncResult BeginInvoke(this Control control, Delegate del, object[] args, AsyncCallback callback, object state) 
{ 
    CustomAsyncResult asyncResult = new CustomAsyncResult(callback, state); 
    control.BeginInvoke(delegate 
    { 
     del.DynamicInvoke(args); 
     asyncResult.Complete(); 
    }, args); 

    return asyncResult; 
} 

public static void EndInvoke(this Control control, IAsyncResult asyncResult) 
{ 
    asyncResult.EndInvoke(); 
} 

Bạn sẽ cần phải xác định lớp CustomAsyncResult của bạn, bạn có thể nhận được tài liệu hướng dẫn về cách để làm điều này here

+0

Cảm ơn. RefreshCompleted có tham số IAsyncResult mặc dù tôi không có quyền truy cập vào thời điểm đó. Ngoài ra tôi muốn RefreshCompleted được gọi là AsyncCallback – Sheed

Các vấn đề liên quan