2011-06-21 41 views
5

thể trùng lặp:
How to block Winforms UI while background thread is runninglàm thế nào để khóa GUI ứng dụng trong C# Winform

tôi đang sử dụng một C# WinForm ứng dụng

Tôi có một nút Save trên màn hình nơi dữ liệu của màn hình được lưu vào cơ sở dữ liệu.

điều gì xảy ra, khi người dùng nhấp vào ứng dụng nút chuyển đến cơ sở dữ liệu và lưu dữ liệu. phải mất một thời gian.

nhưng có nghĩa là trong khi nếu sử dụng một lần nữa nhấn vào nút Save the kiện Click được đánh bắt và khi Click đầu tiên trở lại mã chính (sau khi tiết kiệm cơ sở dữ liệu) sự kiện bắt bị sa thải ..

Nói tóm lại sự kiện click bị bắt và bị sa thải khi luồng trả về từ sự kiện đầu tiên (Tôi đã thử kịch bản bật/tắt nút).

Làm cách nào để ngăn chặn hành vi này.

Kính trọng, Akhil

@Jalal: Tôi đã thử mã này với một số sửa đổi như

private readonly object _userActivityLocker = new object(); 
     private void btnSave_Click(object sender, EventArgs e) 
     { 
      if (System.Threading.Monitor.TryEnter(_userActivityLocker)) 
      { 
       //note that any sub clicks will be ignored while we are here 
       try 
       { 
        DateTime dt = DateTime.Now; 
        Thread.Sleep(2000); 
        Debug.Print("FirstClick {0} Second Click {1}",dt.ToLongTimeString(), DateTime.Now.ToLongTimeString()); 
        //here it is safe to call the save and you can disable the btn 
        Application.DoEvents(); 
       } 
       finally 
       { 
        System.Threading.Monitor.Exit(_userActivityLocker); 
        //re-enable the btn if you disable it. 
       } 
      } 
     } 

nhưng khi tôi nhanh chóng bấm vào nút (i kiểm tra với 5 lần nhấp chuột nhanh chóng) 5 nhấp chuột sự kiện đã bị sa thải và cửa sổ bảng điều khiển đang hiển thị

FirstClick 1:30:22 PM Thứ hai Nhấp 1:30:24 PM FirstClick 1:30 : 24 PM Thứ Hai Nhấp vào 1:30:26 PM FirstClick 1:30:26 PM Thứ hai Nhấp vào 1:30:28 PM FirstClick 1:30:28 PM Thứ hai Nhấp 1:30:30 PM FirstClick 1:30:30 PM Thứ Hai Nhấp vào 1:30:32 PM

+0

truy cập vào liên kết này http://stackoverflow.com/questions/648255/how-to-block-winforms-ui-while-background-thread-is-running – Vamsi

+0

Lạ ... À nên đã làm việc – V4Vendetta

+0

@ V4Vendetta: đây là ** dự kiến ​​** sẽ xảy ra .. xem câu trả lời của tôi để biết thêm thông tin. –

Trả lời

1

Bằng cách bật, sau đó bật lại một lần nữa như bạn đã bỏ qua. Vấn đề với điều này là gì?

public void SaveButton_Click(..., ...) 
{ 
    this.SaveButton.Enabled = false; 
    Save(); 
    this.SaveButton.Enabled = true; 
} 
+0

Để ngăn dữ liệu được lưu hai lần, bạn cũng có thể thêm Boolean [dataSaved], intialize nó thành false. Trước khi lưu dữ liệu, bạn sẽ kiểm tra biến Boolean: nếu sai, sau đó lưu, khác sẽ hiển thị cảnh báo cho biết dữ liệu đã được lưu. Tiếp tục, bạn cũng có thể đặt biến boolean cờ thành false bất cứ khi nào người dùng cập nhật dữ liệu trong tập dữ liệu của bạn/dữ liệu có thể đặt để người dùng có thể lưu lại dữ liệu đã sửa đổi. –

+0

@Josh Smeaton: tôi đã thử công cụ bật/tắt. – Akhil

+0

@ Moez: là giải pháp nào khác ..? – Akhil

1

sử dụng một lớp System.Threading.Monitor sẽ làm các trick như:

private readonly object _userActivityLocker = new object(); 


private void btnSave_Click(object sender, EventArgs e) 
{ 
    new Thread(delegate() 
    { 
    if (System.Threading.Monitor.TryEnter(_userActivityLocker)) 
    { 
     //note that any sub clicks will be ignored while we are here 
     try 
     { 
      //here it is safe to call the save and you can disable the btn 
     } 
     finally 
     { 
      System.Threading.Monitor.Exit(_userActivityLocker); 
      //re-enable the btn if you disable it. 
     } 
    } 
    }) { IsBackground = true }.Start(); 
} 

Để chứng minh rằng việc thay đổi nút để kích hoạt hoặc vô hiệu hóa nhà nước là không đủ ở đây một thử nghiệm đơn giản:

Thêm mới biểu mẫu và thêm một button1 vào nó và bên trong trình xử lý sự kiện nhấn button1 viết mã sau:

private void button1_Click(object sender, EventArgs e) 
{ 
    button1.Enabled = false; 

    Console.WriteLine("First Message"); 

    Thread.Sleep(2000); 

    Console.WriteLine("second Message"); 

    button1.Enabled = true; 
} 

và sau đó xây dựng và chạy các ứng dụng, nhấp đúp vào button1 và kết quả int cửa sổ đầu ra sẽ là:

First Message 
second Message 
First Message 
second Message 

vì vậy chúng tôi phải đảm bảo rằng chỉ một cú nhấp chuột được thực hiện ngay cả khi nhấp đúp chuột vào hoặc lâu hơn và rằng thực hiện đơn giản bằng cách sử dụng cập nhật System.Threading.Monitor

: Lưu ý rằng bạn có thể sử dụng một Task "nếu C# 4.0", một ThreadPool.QueueUserWorkItem hoặc BackgroundWorker như là một thay của chủ đề.

+0

tôi đã sửa đổi câu hỏi, xin hãy xem – Akhil

+0

@kkhil: Tôi quên bọc mã trong một Chủ đề mới .. lỗi của tôi. tuy nhiên tôi sẽ cập nhật câu trả lời của mình. –

+0

@Akhil: Bạn đã thử điều này sau khi chỉnh sửa chưa? Nó có phục vụ nhu cầu của bạn không? –

5

Vấn đề là chương trình của bạn đã chết trên thế giới trong khi nó đang lưu dữ liệu vào cơ sở dữ liệu. Nhấp chuột của người dùng đang nằm trong hàng đợi tin nhắn, chờ chuỗi giao diện người dùng của bạn trở lại với cuộc sống. Khi đó, nút không còn bị vô hiệu hóa do đó sự kiện Nhấp chuột kích hoạt.

Bạn có thể giải quyết nó bằng cách làm sạch các hàng đợi thông điệp trước khi bạn kích hoạt lại các nút để bấm được xử lý trong khi nút bị vô hiệu hóa:

private void button1_Click(object sender, EventArgs e) { 
     button1.Enabled = false; 
     // Save data to database 
     //... 
     System.Threading.Thread.Sleep(2000); 

     Application.DoEvents(); // Empty the message queue 
     if (!button1.IsDisposed) button1.Enabled = true; 
    } 

Đừng bỏ qua kiểm tra IsDisposed, DoEvents là nguy hiểm bởi vì nó không chọn lọc về những sự kiện nào được xử lý. Nó sẽ vui vẻ cho phép người dùng đóng cửa sổ chính trong khi mã của bạn vẫn đang chạy.

Nhưng giải pháp tốt hơn là không để cho chuỗi giao diện người dùng của bạn bị chết như thế này. Sử dụng một BackgroundWorker để thực hiện lưu trên một chuỗi công nhân. Điều này cũng sẽ tránh được cửa sổ ghost "Not Responding" xấu xí mà Windows cài đặt khi quá trình lưu của bạn mất hơn một vài giây. Nó có thể không làm điều này nhưng ngay bây giờ, nhưng nó sẽ là một năm kể từ bây giờ khi dbase đã phát triển. Bạn có thể kích hoạt lại nút trong trình xử lý sự kiện RunWorkerCompleted của BGW.

+0

@Hans Passant: nếu người dùng nhấn nút hai lần quá nhanh, bật/tắt sẽ không giúp được, điều này có thể xảy ra khi máy chủ tải quá ... –

+0

Hmm, bạn rõ ràng không thử mã này. –

+0

@Hans Passant: không, chắc chắn tôi đã thử nghiệm điều này, bạn có thể kiểm tra nó ngay bây giờ, chỉ cần làm như sau: tạo một Biểu mẫu mới và thêm button1 vào nó và trên trình xử lý sự kiện bấm của nó dán mã này 'button1.Enabled = sai; Console.WriteLine ("Thư đầu tiên"); Thread.Sleep (2000); Console.WriteLine ("thông báo thứ hai"); button1.Enabled = true; 'và tất nhiên bạn phải nhấp đúp vào nút hoặc nhấp nhanh hai lần để xem phương thức sẽ được gọi hai lần ..... đầu ra ** sẽ là ** Thư đầu tiên Thư thứ hai Tin nhắn đầu tiên Tin nhắn thứ hai –

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