2012-12-31 38 views
5

Tôi đang cố gắng để có một chuỗi riêng biệt trong ứng dụng WinForms C# bắt đầu một nhân viên nền điều khiển ProgressBar (vùng chọn). Vấn đề là khi tôi cố gắng để thiết lập thanh để nhìn thấy nó chỉ không làm gì, và tôi đã thử nhiều hình thức Invoke nhưng họ dường như không giúp đỡ.Thao tác các phần tử giao diện người dùng từ một chủ đề khác

Phương pháp sau progressBarCycle được gọi từ một chuỗi riêng biệt.

BackgroundWorker backgroundWorker = new BackgroundWorker(); 

    public void progressBarCycle(int duration) 
    { 
     backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); 
     backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); 
     backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted); 
     backgroundWorker.WorkerReportsProgress = true; 
     backgroundWorker.WorkerSupportsCancellation = true; 
     backgroundWorker.RunWorkerAsync(duration); 
    } 

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 

     worker.ReportProgress(0); 

     DateTime end = DateTime.Now.AddMilliseconds((int)e.Argument); 
     while (DateTime.Now <= end) 
     { 
      System.Threading.Thread.Sleep(1000); 
     } 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (!this.IsHandleCreated) 
      this.CreateHandle(); 
     statusStrip1.Invoke((MethodInvoker)delegate 
     { 
      progressBar1.Visible = false; 
     }); 
     // if (!this.IsHandleCreated) 
     // { 
     //  this.CreateHandle(); 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false)); 
     //  else progressBar1.Visible = false; 
     // } 
     // else 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false)); 
     //  else progressBar1.Visible = false; 
    } 

    private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     if (!this.IsHandleCreated) 
      this.CreateHandle(); 
     statusStrip1.Invoke((MethodInvoker)delegate 
     { 
      progressBar1.Visible = true; 
     }); 
     // if (!this.IsHandleCreated) 
     // { 
     //  this.CreateHandle(); 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true)); 
     //  else progressBar1.Visible = true; 
     // } 
     // else 
     //  if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true)); 
     //  else progressBar1.Visible = true; 
    } 

Tôi có thiếu điều gì đó hiển nhiên ở đây không? Các phần bình luận là những thứ khác tôi đã thử.

Trả lời

6

ProgressChangedđã được nâng lên trên chuỗi giao diện người dùng (thông qua bối cảnh đồng bộ hóa); ProgressChanged của bạn không cần phải làm điều đó Invoke - nó có thể thao tác trực tiếp giao diện người dùng (ngược lại, DoWork hoàn toàn có thể không làm điều đó). Có lẽ vấn đề thực sự là bạn không làm bất kỳ số nào trong số worker.ReportProgress(...)bên trong vòng lặp - vì vậy nó chỉ xảy ra một lần khi bắt đầu.

Dưới đây là một ví dụ đầy đủ:

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 
static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     using (var worker = new BackgroundWorker { 
      WorkerReportsProgress = true }) 
     using (var progBar = new ProgressBar { 
      Visible = false, Step = 1, Maximum = 100, 
      Dock = DockStyle.Bottom }) 
     using (var btn = new Button { Dock = DockStyle.Top, Text = "Start" }) 
     using (var form = new Form { Controls = { btn, progBar } }) 
     { 
      worker.ProgressChanged += (s,a) => { 
       progBar.Visible = true; 
       progBar.Value = a.ProgressPercentage; 
      }; 
      worker.RunWorkerCompleted += delegate 
      { 
       progBar.Visible = false; 
      }; 
      worker.DoWork += delegate 
      { 
       for (int i = 0; i < 100; i++) 
       { 
        worker.ReportProgress(i); 
        Thread.Sleep(100); 
       } 
      }; 
      btn.Click += delegate 
      { 
       worker.RunWorkerAsync(); 
      }; 
      Application.Run(form); 
     } 
    } 
} 
+0

Tôi đặt trong ProgressChanged và RunWorkerCompleted hoàn toàn để xem nếu nó sẽ sửa chữa vấn đề của tôi. Khi tôi đã có tất cả các mã được bao gồm trong DoWork thay vào đó nó sẽ làm điều tương tự.Trạng thái hiển thị sẽ không bao giờ thay đổi, có hoặc không có lời gọi. - Oh và có lẽ đáng nói đến đây là một Marquee nên tôi không có ý định sử dụng các báo cáo tiến độ. – UncleDave

+0

@UncleDave sau đó có vẻ như thanh tiến trình nằm trong một điều khiển khác (bảng điều khiển có thể) không hiển thị. –

+0

Thanh tiến trình ở trên biểu mẫu đang chạy mã này, cũng là biểu mẫu bắt đầu của ứng dụng. – UncleDave

2
  1. Run progressBarCycle từ thread UI. RunWorkerAsync sẽ tạo chủ đề mới cho bạn.
  2. Trong backgroundWorker_ProgressChanged chỉ cần gọi progressBar1.Visible = true;. Không cần Invoke.
  3. Tốt hơn cũng thêm progressBar1.Refresh();.
2

Một khả năng khác cần lưu ý là thanh tiến trình đang chạy trên chuỗi giao diện người dùng của bạn. Để thanh tiến trình được hiển thị và vẽ lại chính nó để hiển thị số lượng tiến trình mới, chuỗi giao diện người dùng phải chạy tự do, xử lý các thông báo cửa sổ trong vòng lặp ứng dụng chính. Vì vậy, nếu bạn bắt đầu chuỗi công nhân nền của bạn nhưng sau đó chuỗi giao diện người dùng của bạn nằm trong vòng chờ đợi chờ đợi để hoàn thành hoặc tắt và tải các công việc khác, thì nó sẽ không xử lý các thông báo của cửa sổ và thanh tiến trình sẽ "không phản hồi". Bạn cần phải phát hành chuỗi giao diện người dùng để cập nhật này vẫn xảy ra (tức là trả về từ trình xử lý sự kiện của bạn và cho phép giao diện người dùng tiếp tục chạy như bình thường).

Sự nguy hiểm của điều này là nếu giao diện người dùng đang hoạt động, thì người dùng vẫn có thể tương tác với nó. Do đó bạn phải viết UI để biết khi nhân viên nền hoạt động, và xử lý tình huống đúng cách (các vấn đề có thể bao gồm: Cho phép người dùng khởi động lại nhân viên nền trong khi nó đang chạy, UI cố gắng hiển thị thông tin trong khi nhân viên thread đang bận rộn cập nhật nó, người dùng quyết định tải một tài liệu mới hoặc thoát trong khi nhân viên nền đang bận, vv). Hai giải pháp chính cho việc này là bao bọc tất cả các giao diện người dùng trong một lá chắn bảo vệ ngăn chặn bất kỳ thứ gì nguy hiểm được khởi tạo trong khi nền hoạt động (điều này có thể là rất nhiều công việc nếu bạn có nhiều điều khiển để bọc theo cách này, và rất dễ mắc lỗi khi cho phép lỗi lướt qua) hoặc để giao diện người dùng "không được bảo vệ" nhưng thêm IMessageFilter để dừng tất cả tương tác "nguy hiểm" của người dùng (nhấp và nhấn) bằng cách tắt thông báo cửa sổ đến (WM_KEYPRESS v.v.) trong khi xử lý nền đang hoạt động.

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