2010-08-07 45 views
5
private void button1_Click(object sender, RoutedEventArgs e) 
{ 
     int i = 0; 

     while (i < 500) 
     { 
      label1.Content = i.ToString(); 
     // System.Threading.Thread.Sleep(2000); 
      ++i; 
     } 
} 

Tôi đang cố gắng cập nhật nội dung của Nhãn mỗi khi biến được tăng lên, nhưng điều xảy ra là nội dung của nhãn1 chỉ thay đổi một lần và chỉ sau khi vòng lặp while kết thúc. Tôi nghĩ rằng sự gia tăng của biến truy cập quá nhanh đến mức luồng giao diện người dùng không thể bắt kịp với nó, vì vậy tôi muốn làm cho luồng không hoạt động trong 2 giây, hy vọng sẽ thấy giá trị thay đổi label1 500 lần. Nó cũng không hoạt động. Tại sao?Nhãn không thay đổi giá trị bên trong một vòng lặp while

Trả lời

7

Như những người khác đã nói, vấn đề là bạn đang chặn chuỗi giao diện người dùng.

Thay vì sử dụng các đề xuất với Application.DoEvents, tôi khuyên bạn nên sử dụng DispatcherTimer để lên lịch cập nhật. Mẫu mã:

DispatcherTimer timer = new DispatcherTimer(); 
timer.Tick += delegate 
{ 
    label.Content = counter.ToString(); 
    counter++; 
    if (counter == 500) 
    { 
     timer.Stop(); 
    } 
}; 
timer.Interval = TimeSpan.FromMilliseconds(2000); 
timer.Start(); 

(Tôi không chắc liệu Application.DoEvents thậm chí làm việc trong WPF, và nó thường được coi là thực hành xấu anyway Nếu nó không làm việc, tôi nghi ngờ rằng nó được bảo đảm để làm việc trong các phiên bản trong tương lai.. trộn hai UIS trong cùng một ứng dụng chỉ vì lợi ích của điều này có vẻ như là một ý tưởng thực sự tồi tệ với tôi.)

bạn thể sử dụng một Timer (hoặc System.Threading.Timer hoặc System.Timers.Timer) nhưng trong cả hai trường hợp sau đó bạn sẽ cần để sắp xếp lại chủ đề của bộ điều phối. DispatcherTimer làm cho việc này trở nên đơn giản hơn - sự kiện Tick luôn được kích hoạt trong luồng Dispatcher.

EDIT: Nếu bạn thực sự muốn một tương đương với DoEvents, đây là mã mà tôi đã xác minh làm việc, và được dựa trên MSDN docs for Dispatcher.PushFrame giải thích làm thế nào để làm tương đương với Application.DoEvents nhưng từ WPF:

public void DoEvents() 
{ 
    DispatcherFrame frame = new DispatcherFrame(); 
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
     new DispatcherOperationCallback(ExitFrames), frame); 
    Dispatcher.PushFrame(frame); 
} 

public object ExitFrames(object f) 
{ 
    ((DispatcherFrame)f).Continue = false; 

    return null; 
} 

private void ButtonClick(object sender, RoutedEventArgs e) 
{ 
    for (int i = 0; i < 500; i++) 
    { 
     label.Content = i.ToString(); 
     DoEvents(); 
    } 
} 

Tôi vẫn chưa đề xuất điều này mặc dù - WPF (như Windows Forms) được thiết kế theo cách hoạt động theo hướng sự kiện.

+0

@drorhan: Chắc chắn, do hẹn giờ. Điều đó dễ dàng điều chỉnh. Tôi đã chọn thời gian do những gì đã được đặt trong câu hỏi, thừa nhận đã nhận xét. Điều chỉnh nó để (nói) một khoảng 20ms sẽ làm cho nó đếm khá nhanh chóng. –

+0

@drorhan: (Bây giờ bạn đã chỉnh sửa bình luận) Điều này mất khoảng 500 x 2s (1000 giây, chỉ dưới 17 phút) để hoàn thành. Điều đó phản ánh những gì OP nói về việc trì hoãn 2 giây. –

+0

@drorhan: Tại sao không? Đó là 2000ms (2 giây) và nó dừng sau 500 lần lặp lại. Tôi sẽ không ngạc nhiên khi thấy nó dài hơn một chút trong thực tế, nhưng nó phải là * gần đúng *. –

0

Trình xử lý đang hogging vòng lặp sự kiện.

Một tùy chọn là sử dụng phương pháp Application.DoEvents để cho phép các thao tác vẽ thực thi. Lưu ý rằng đây được coi là thực hành không tốt, như Thread.Sleep sẽ làm cho giao diện người dùng không phản hồi.

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
     int i = 0; 

     while (i < 500) 
     { 
      label1.Content = i.ToString(); 
      System.Threading.Thread.Sleep(2000); 
      Application.DoEvents(); 
      ++i; 
     } 
} 

Một tùy chọn khác là sử dụng Control.BeginInvoke cho mỗi hoạt động label1.Content = ...

+0

'BeingInvoke' sẽ không hoạt động. Chuỗi giao diện người dùng vẫn bị chặn. –

+0

Tôi nên đã làm cho nó rõ ràng hơn: thực hiện vòng lặp trên một chủ đề khác, làm cho giao diện người dùng cập nhật với Control.BeginInvoke. – Ani

0

Cập nhật phương pháp của bạn như thế này:

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
     int i = 0; 

     while (i < 500) 
     { 
      label1.Content = i.ToString(); 
      //Update the UI 
      Application.DoEvents(); 
     // System.Threading.Thread.Sleep(2000); 
      ++i; 
     } 
} 

Đây là phương pháp đơn giản nhất để làm điều này. Nhưng không phải là ưu tiên. Người được ưu tiên sẽ sử dụng số backgroundworker cho nhu cầu đó.

0

"Nếu chúng tôi xử lý toàn bộ tìm kiếm bên trong trình xử lý sự kiện nhấp của nút, chúng tôi sẽ không bao giờ cung cấp cho chuỗi giao diện người dùng cơ hội xử lý các sự kiện khác. Giao diện người dùng sẽ không thể trả lời thông báo nhập hoặc xử lý. không bao giờ sơn lại và không bao giờ trả lời các nhấp chuột vào nút. " - Source.

Dù sao, những gì bạn sẽ muốn làm là tạo một chuỗi công nhân thực hiện vòng lặp của bạn và chỉ cập nhật giao diện người dùng bằng Dispatcher.BeginInvoke.

-1

Tôi nghĩ rằng những gì bạn cần là

Label.Update(); 
Các vấn đề liên quan