2009-07-30 48 views
17

Tôi nghĩ rằng tôi cần một số giải thích về WPFs Dispatcher.InvokeDispatcher.BeginInvoke cách sử dụng.WPF Dispatcher.BeginInvoke và UI/Chủ đề nền

Giả sử tôi có một số dài chạy 'công việc' mã như thế này mà được gọi trên báo chí của một nút trong một ứng dụng WPF đơn giản:

longWorkTextBox.Text = "Ready For Work!"; 
Action workAction = delegate 
    { 
    Console.WriteLine("Starting Work Action"); 
    int i = int.MaxValue; 
    while (i > 0) 
     i--; 
    Console.WriteLine("Ending Work Action"); 
    longWorkTextBox.Text = "Work Complete"; 
    }; 
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction); 

Mã này được khóa lên giao diện người dùng của tôi trong khi workAction đang được thực hiện. Điều này là do Dispatcher gọi luôn luôn chạy trên thread UI, phải không?

Giả sử điều này, thực tiễn tốt nhất để định cấu hình điều phối viên của tôi là thực thi hành động làm việc trong một chuỗi riêng biệt từ giao diện người dùng của tôi là gì? Tôi biết tôi có thể thêm một BackgroundWorker-workAction tôi để ngăn chặn UI của tôi từ khóa như vậy:

longWorkTextBox.Text = "Ready For Work!"; 
Action workAction = delegate 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += delegate 
    { 
     Console.WriteLine("Starting Slow Work"); 
     int i = int.MaxValue; 
     while (i > 0) 
     i--; 
     Console.WriteLine("Ending Work Action"); 
    }; 
    worker.RunWorkerCompleted += delegate 
    { 
     longWorkTextBox.Text = "Work Complete"; 
    }; 
    worker.RunWorkerAsync(); 
}; 
longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction); 

Có cách nào tao nhã hơn để làm điều này bên cạnh việc sử dụng BackgroundWorker? Tôi đã luôn luôn nghe rằng BackgroundWorker là kỳ quặc, vì vậy tôi tò mò muốn biết một số lựa chọn thay thế.

Trả lời

24

Tôi thành thật nghĩ rằng BackgroundWorker là giải pháp thanh lịch nhất cho việc này. Tôi không thể nghĩ ra một cách đơn giản hơn để làm điều đó.

+0

Đúng. Bạn muốn làm một cái gì đó trong một chủ đề nền, nhưng ảnh hưởng đến các chủ đề giao diện người dùng khi bạn đã hoàn tất, BackgroundWorker là khá nhiều điều dễ nhất để sử dụng. –

+1

Trong năm 2013, có một cách đơn giản hơn - không đồng bộ/chờ đợi: http://stackoverflow.com/questions/12654968/is-async-and-await-exclusively-for-gui-based-asynchronous-programming – Cosmin

+0

Tôi thực sự thích không đồng bộ, nhưng nó không hoạt động tốt với mọi thứ. Nhưng nó là một lựa chọn khá tốt! – Malavos

4

Câu trả lời của Charlie là những gì bạn đang tìm kiếm.

Tuy nhiên, nếu có thể bạn có thể xem liệu bạn có thể xếp dỡ công việc của mình để các đơn vị công việc riêng lẻ nhỏ và không ảnh hưởng đến giao diện người dùng nhiều hay không. Điều này sẽ cho phép bạn chỉ cần sử dụng Dispatcher trực tiếp. Có một ví dụ điển hình về điều này trên trang WPF Threading: https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx

+0

Bài viết tuyệt vời về chủ đề này! –

+0

Yeah ... thường MSDN là mức tối thiểu, nhưng bài viết đó thực sự là kỹ lưỡng. –

+0

để biết thêm thông tin về lập trình không đồng bộ trong WPF, bạn có thể đọc: http://www.a2zdotnet.com/View.aspx?Id=93. – frameworkninja

2

Vì tên của nó cho biết nó sẽ thực hiện trong nền do đó bạn không cần phải khởi tạo nó với Dispatcher. Thêm vào đó nếu bạn muốn mã này chạy vào WP7 thì BeginInvoke không nhận được tham số nền.

Tôi đề nghị là để tạo ra các BackgroundWorker như:

BackgroundWorker worker = new BackgroundWorker; 

Và sau đó tạo ra các trình xử lý cho các sự kiện:

worker.WorkerReportsProgress = true; 
worker.WorkerSupportsCancellation = true; 
worker.DoWork +=new DoWorkEventHandler(worker_DoWork); 
worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged); 

Và cuối cùng bạn gọi:

bkwkPlayingLoop.RunWorkerAsync(); 

Đó là một sự cám dỗ lớn khi sử dụng Dispatcher từ bên trong DoWork nhưng thay vào đó hãy gọi worker.ReportProgress() và xử lý giao diện người dùng từ đó. Bạn sẽ phải đối mặt với một số mâu thuẫn với việc bắn các sự kiện chấm dứt.

4

Tôi cũng không thích BackgroundWorker. Một giải pháp thay thế đơn giản có thể là một cái gì đó như:

using System; 
using System.Threading; 
using System.Windows; 

namespace Sample 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnSourceInitialized(EventArgs e) 
     { 
      base.OnSourceInitialized(e); 
      longWorkTextBox.Text = "Ready For Work!"; 
     } 

     private void startButton_Click(object sender, RoutedEventArgs e) 
     { 
      new Thread(Work).Start(); 
     } 

     void Work() 
     { 
      longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; })); 
      Console.WriteLine("Starting Work Action"); 
      int i = int.MaxValue; 
      while (i > 0) 
       i--; 
      Console.WriteLine("Ending Work Action"); 
      longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; })); 
     } 
    } 
} 

Dễ dàng, phải không?

0

Nhiệm vụ dễ sử dụng hơn Công nhân nền, làm nhiều việc hơn, có ít vấn đề hơn và được tạo khá nhiều nên không cần sử dụng nền nữa ...

+0

Bạn có thể sử dụng một số ví dụ? – LuckyLikey

+0

Nhân viên cơ bản đã bị Công việc không còn được dùng nữa ... http://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-intro.html – MattE

+0

Vâng .. cảm ơn vì liên kết này. Tôi đang đánh giá một cách tốt đẹp để thực hiện các backgroundtask trong WPF. Tôi đoán tôi sẽ có một cái nhìn sâu sắc về loạt bài này. – LuckyLikey

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