2010-12-13 38 views
9

Tôi có một nút trên biểu mẫu cửa sổ gọi phương thức RunWorkerAsync(), điều này sẽ thực hiện hành động sau đó cập nhật ListBox trên cùng một biểu mẫu.Truy cập Kiểm soát giao diện người dùng từ Chủ đề nền tảng

Sau sự kiện DoWork đã xong tôi giao Kết quả cho sự kiện này (Đó là một danh sách), tôi xử lý() sự kiện RunWorkerCompleted và sau đó thực hiện đoạn mã sau để cập nhật Listbox tôi

alt text

trong đó kêu gọi này:

alt text

(Xin lỗi, mã định dạng sẽ không hoạt động)

Bây giờ khi tôi chạy các ứng dụng và nhấn nút refresh các ngoại lệ sau đây xuất hiện:

alt text

Làm thế nào tôi có thể làm được việc này?

Edit:

Trường hợp ngoại lệ được ném vào báo cáo kết quả folowing, điều này xảy ra trong phương pháp DoWork nơi tôi xóa các nội dung để giữ cho danh sách cập nhật;

listBoxServers.Items.Clear();

+0

Đây có phải là WPF hoặc Windows Forms không? Và trên dòng nào bạn có ngoại lệ này? – decyclone

+0

@ decyclone Tôi đã cập nhật câu hỏi của mình với nhiều thông tin hơn –

+0

Tôi đề nghị câu trả lời của Chris. – decyclone

Trả lời

10

Dưới đây là một đoạn mà tôi thấy rất tiện dụng:

public static void ThreadSafe(Action action) 
{ 
    Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, 
     new MethodInvoker(action)); 
} 

Bạn có thể vượt qua nó bất kỳ đại biểu nào của Action loại hoặc đơn giản là một lambda như thế này:

ThreadSafe(() => 
{ 
    [your code here] 
}); 

hoặc

ThreadSafe(listBoxServers.Items.Clear); 
+1

Đây có phải là WPF không? WinForms có hỗ trợ Dispatcher không? –

+0

'Dispatcher' là một lớp WPF nhưng mã này sẽ làm việc với WinForms. – Nobody

+0

Đối với một số lý do Dispatcher không được nhận diện bởi IntelliSense, tôi có câu lệnh System.Threading được bao gồm. –

1

Chủ đề nền không được phép cập nhật giao diện người dùng trong ứng dụng Windows, vì vậy bạn phải hoàn nguyên kiểm soát quay lại luồng giao diện người dùng để cập nhật thực tế.

Tạo một phương pháp mà sẽ gọi UpdateServerDetails về các chủ đề chính như sau:

private void DispatchServerDetails(List<ServerDetails> details) 
{ 
    Action<List<ServerDetails>> action = UpdateServerDetails; 
    Dispatcher.Invoke(action) 
} 

và sau đó gọi DispatchServerDetails thay vì UpdateServerDetails.

Một số hãy cẩn thận:
-Đây hoạt động tốt nhất trong các ứng dụng WPF, cho WinForms, bạn sẽ cần phải nhảy qua một số hoops, hoặc bạn có thể sử dụng InvokeRequired
-Các UI cập nhật vẫn còn đồng bộ, vì vậy nếu UpdateServerDetails hiện một rất nhiều công việc, nó sẽ chặn các thread UI (không phải trường hợp của bạn, chỉ để được ở bên an toàn).

+0

Bạn có thể cung cấp một ví dụ không? –

+0

@Jamie http://stackoverflow.com/questions/975087/wpf-dispatcher-and-running-it-in-background – jan

+1

Đó không phải là lớp WPF phải không? – CodesInChaos

9

Những gì tôi đã làm là một cái gì đó như thế này mỗi khi bạn cần phải chạy một cái gì đó trên đề:

listBoxServers.BeginInvoke(
    (Action) 
    (() => listBoxServers.Items.Clear())); 
+0

Vấn đề Lisp điển hình, có vẻ như thiếu paren. –

+0

Cảm ơn Hans, tôi đã thêm dấu ngoặc kép còn thiếu. –

13

Bạn không được gọi Invoke trên hộp danh sách, nhưng trên biểu mẫu.Đối với các ứng dụng WinForms tôi sử dụng một cái gì đó như:

... 
this.Invoke((MethodInvoker)delegate() 
{ 
    // Do stuff on ANY control on the form. 
}); 
... 

Tùy thuộc vào phiên bản .NET, bạn có thể phải khai báo một delegate cho MethodInvoker mình là

public delegate void MethodInvoker(); 

Tuy nhiên, bạn cũng có thể cân nhắc sử dụng tính năng ReportProgress của Nhân viên nền. Trình xử lý sự kiện tương ứng sẽ được gọi trong ngữ cảnh của chuỗi của biểu mẫu.

1

Sử dụng Gọi trong dự án biểu mẫu cửa sổ có thể hơi phức tạp, có một số cạm bẫy được ghi lại nhưng dễ bỏ sót. Tôi khuyên bạn nên sử dụng một cái gì đó như bạn sẽ tìm thấy trong câu hỏi này:

Is it appropriate to extend Control to provide consistently safe Invoke/BeginInvoke functionality?

Nó xử lý trường hợp invoke là không bắt buộc, được gọi là từ chủ đề khác nhau, xử lý được hoặc không được tạo ra, etcetcetc. Nó có thể dễ dàng sửa đổi thành SafeInvoke()SafeBeginInvoke() nếu bạn không phải là người hâm mộ tham số bool.

(bao gồm vào đây để thuận tiện cho bạn:

/// Usage: 
this.lblTimeDisplay.SafeInvoke(() => this.lblTimeDisplay.Text = this.task.Duration.ToString(), false); 

// or 
string taskName = string.Empty; 
this.txtTaskName.SafeInvoke(() => taskName = this.txtTaskName.Text, true); 


/// <summary> 
/// Execute a method on the control's owning thread. 
/// </summary> 
/// <param name="uiElement">The control that is being updated.</param> 
/// <param name="updater">The method that updates uiElement.</param> 
/// <param name="forceSynchronous">True to force synchronous execution of 
/// updater. False to allow asynchronous execution if the call is marshalled 
/// from a non-GUI thread. If the method is called on the GUI thread, 
/// execution is always synchronous.</param> 
public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous) 
{ 
    if (uiElement == null) 
    { 
     throw new ArgumentNullException("uiElement"); 
    } 

    if (uiElement.InvokeRequired) 
    { 
     if (forceSynchronous) 
     { 
      uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); 
     } 
     else 
     { 
      uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); 
     } 
    } 
    else 
    { 
     if (!uiElement.IsHandleCreated) 
     { 
      // Do nothing if the handle isn't created already. The user's responsible 
      // for ensuring that the handle they give us exists. 
      return; 
     } 

     if (uiElement.IsDisposed) 
     { 
      throw new ObjectDisposedException("Control is already disposed."); 
     } 

     updater(); 
    } 
} 
0

Tôi chỉ tìm ra một cách đơn giản mà không cần dùng Invoke:

int fakepercentage = -1; 
//some loop here......if no loop exists, just change the value to something else 
if (fakepercentage == -1) 
{ 
    fakepercentage = -2; 
} 
else 
{ 
    fakepercentage = -1; 
} 
backgroundworker1.ReportProgress(fakepercentage); 

Sau đó, trong backgroundworker1_ProgressChanged (object sender, ProgressChangedEventArgs e):

if (e.ProgressPercentage < 0) 
{ 
    //access your ui control safely here 
} 
Các vấn đề liên quan