2009-04-08 15 views
9

Sự kiện SelectedIndexChanged bị sa thải trong ứng dụng của tôi từ một combo box khi:Phân biệt giữa các sự kiện nêu ra bởi tương tác người dùng và mã của riêng tôi

  1. người dùng chọn một mục khác nhau trong combo box, hoặc khi:
  2. mã của riêng tôi cập nhật hộp của hộp tổ hợp để phản ánh rằng hộp tổ hợp hiện đang hiển thị thuộc tính cho một đối tượng khác.

Tôi quan tâm đến sự kiện SelectedIndexChanged đối với trường hợp 1, để tôi có thể cập nhật các thuộc tính của đối tượng hiện tại. Nhưng trong trường hợp 2, tôi không muốn sự kiện cháy, bởi vì các thuộc tính của đối tượng đã không thay đổi.

Ví dụ có thể hữu ích. Hãy xem xét rằng tôi có một hộp danh sách có chứa một danh sách những người và tôi có một hộp kết hợp đại diện cho quốc tịch của người hiện đang được chọn trong danh sách. Trường hợp 1 có thể xảy ra nếu Fred hiện đang được chọn trong danh sách, và tôi sử dụng hộp kết hợp để thay đổi quốc tịch của mình từ tiếng Anh sang tiếng xứ Wales. Trường hợp 2 có thể xảy ra nếu tôi chọn Bob, người Scotland, trong danh sách. Ở đây, danh sách mã xử lý sự kiện cập nhật danh sách của tôi thấy rằng Bob hiện được chọn và cập nhật hộp kết hợp để Scotland hiện là mục được chọn. Điều này làm cho sự kiện SelectedIndexChanged của combo box được kích hoạt để thiết lập quốc tịch của Bob cho người Scotland, mặc dù nó đã là người Scotland.

Làm cách nào để cập nhật thuộc tính SelectedItem của combo box mà không gây ra sự kiện SelectedIndexChanged để kích hoạt? Một cách sẽ là hủy đăng ký trình xử lý sự kiện, đặt SelectedItem, sau đó đăng ký lại trình xử lý sự kiện, nhưng điều này có vẻ tẻ nhạt và dễ bị lỗi. Phải có cách tốt hơn.

Trả lời

7

Tôi đã tạo một lớp tôi gọi là SuspendLatch. Cung cấp trên một tên tốt hơn được hoan nghênh, nhưng nó làm những gì bạn cần và bạn sẽ sử dụng nó như thế này:

void Method() 
{ 
    using (suspendLatch.GetToken()) 
    { 
     // Update selected index etc 
    } 
} 

void listbox1_SelectedIndexChanged(object sender, EventArgs e) 
{ 
    if (suspendLatch.HasOutstandingTokens) 
    { 
     return; 
    } 

    // Do some work 
} 

Nó không đẹp, nhưng nó không làm việc, và không giống như unregistering các sự kiện hoặc cờ boolean, nó hỗ trợ các hoạt động lồng nhau một chút như TransactionScope. Bạn tiếp tục lấy mã thông báo từ chốt và chỉ khi mã thông báo cuối cùng được xử lý mà số HasOutstandingTokens trả về false. Đẹp và an toàn. Không threadsafe, mặc dù ...

Dưới đây là các mã cho SuspendLatch:

public class SuspendLatch 
{ 
    private IDictionary<Guid, SuspendLatchToken> tokens = new Dictionary<Guid, SuspendLatchToken>(); 

    public SuspendLatchToken GetToken() 
    { 
     SuspendLatchToken token = new SuspendLatchToken(this); 
     tokens.Add(token.Key, token); 
     return token; 
    } 

    public bool HasOutstandingTokens 
    { 
     get { return tokens.Count > 0; } 
    } 

    public void CancelToken(SuspendLatchToken token) 
    { 
     tokens.Remove(token.Key); 
    } 

    public class SuspendLatchToken : IDisposable 
    { 
     private bool disposed = false; 
     private Guid key = Guid.NewGuid(); 
     private SuspendLatch parent; 

     internal SuspendLatchToken(SuspendLatch parent) 
     { 
      this.parent = parent; 
     } 

     public Guid Key 
     { 
      get { return this.key; } 
     } 

     public override bool Equals(object obj) 
     { 
      SuspendLatchToken other = obj as SuspendLatchToken; 

      if (other != null) 
      { 
       return Key.Equals(other.Key); 
      } 
      else 
      { 
       return false; 
      } 
     } 

     public override int GetHashCode() 
     { 
      return Key.GetHashCode(); 
     } 

     public override string ToString() 
     { 
      return Key.ToString(); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
      if (!disposed) 
      { 
       if (disposing) 
       { 
        // Dispose managed resources. 
        parent.CancelToken(this); 
       } 

       // There are no unmanaged resources to release, but 
       // if we add them, they need to be released here. 
      } 
      disposed = true; 

      // If it is available, make the call to the 
      // base class's Dispose(Boolean) method 
      //base.Dispose(disposing); 
     } 
    } 
} 
+0

Tôi có thể cho tôi. Nhưng đây không phải là một chút overkill - "Làm thế nào tôi có thể cập nhật tài sản SelectedItem combo box của tôi mà không gây ra sự kiện SelectedIndexChanged để bắn?" Tôi có thể có phản hồi về giải pháp của mình không? –

+0

Nó có thể là quá mức cần thiết. Đặt cờ hoặc loại bỏ eventhandler là một giải pháp đơn giản hơn, nhưng tôi đã tạo ra SuspendLatch khi tôi nhận ra rằng tôi có nhiều nơi mà tôi muốn tạm dừng sự kiện SelectedIndexChanged –

+0

"Chào mừng một tên tốt hơn" - lớp công khai SuspendThingerMajig? – Juliet

4

Tôi nghĩ rằng cách tốt nhất là nên sử dụng một biến cờ:

bool updatingCheckbox = false; 

void updateCheckBox() 
{ 
    updatingCheckBox = true; 

    checkbox.Checked = true; 

    updatingCheckBox = false; 
} 

void checkbox_CheckedChanged(object sender, EventArgs e) 
{ 
    if (!updatingCheckBox) 
     PerformActions() 
} 

[Chỉnh sửa: chỉ gửi bài mã không thực sự rõ ràng]

Trong trường hợp này, trình xử lý sự kiện sẽ không thực hiện các hoạt động bình thường khi hộp kiểm được thay đổi thông qua updateCheckBox().

+0

Đây là phiên bản đơn giản hơn của SuspendLatch của tôi (được đăng ở đâu đó trên câu hỏi này) và phương pháp tôi đã sử dụng cho đến khi tôi nhận ra mình có nhiều nơi mà tôi muốn tạm dừng sự kiện SelectedIndexChanged. –

+0

Thật vậy, upvoted câu trả lời của bạn kể từ khi tôi có thể sẽ sử dụng ý tưởng đó bản thân mình;) – Lennaert

+0

Vui mừng đó là một số sử dụng. Rất vui khi có ít nhất một ý tưởng hay trong một thời gian :) –

1

Tôi để sự kiện kích hoạt. Nhưng, tôi đặt cờ trước khi thay đổi chỉ mục và lật lại sau. Trong trình xử lý sự kiện, tôi kiểm tra xem cờ có được đặt và thoát khỏi trình xử lý nếu có.

1

Tôi nghĩ bạn nên tập trung vào đối tượng chứ không phải sự kiện đang diễn ra.

Nói ví dụ bạn có sự kiện

void combobox_Changed(object sender, EventArgs e) 
{ 
    PerformActions() 
} 

và PerformActions đã làm cái gì đó để tác động của

void PerformActions() 
{ 
    (listBox.SelectedItem as IPerson).Nationality = 
     (comboBox.SelectedItem as INationality) 
} 

sau đó bên trong người bạn sẽ mong đợi để xem một cái gì đó để tác động của

class Person: IPerson 
{ 
    INationality Nationality 
    { 
     get { return m_nationality; } 
     set 
     { 
      if (m_nationality <> value) 
      { 
      m_nationality = value; 
      this.IsDirty = true; 
      } 
     } 
    } 
} 

điểm ở đây là bạn để cho đối tượng theo dõi những gì đang xảy ra với chính nó, không phải giao diện người dùng. Điều này cũng cho phép bạn theo dõi việc theo dõi cờ bẩn trên các đối tượng của bạn, điều này có thể hữu ích cho sự kiên trì sau này.

Điều này cũng giúp giao diện người dùng của bạn sạch sẽ và ngăn không cho mã đăng ký sự kiện lẻ có thể dễ xảy ra lỗi nhất.

3

Tôi đã luôn sử dụng biến cờ boolean để bảo vệ khỏi trình xử lý sự kiện không mong muốn. Ứng dụng mẫu TaskVision đã dạy tôi cách thực hiện việc này.

sự kiện của bạn mã xử lý cho tất cả các sự kiện của bạn sẽ trông như thế này:

private bool lockEvents; 

protected void MyEventHandler(object sender, EventArgs e) 
{ 
    if (this.lockEvents) 
    { 
     return; 
    } 

    this.lockEvents = true; 
    //Handle your event... 
    this.lockEvents = false; 
} 
0

tôi cuối cùng đã tìm thấy một giải pháp để tránh những sự kiện uncessary khỏi bị sa thải quá nhiều thời gian.

Tôi sử dụng bộ đếm và tôi chỉ móc/mở các sự kiện tôi muốn đeo mặt nạ một lần khi không cần thiết và khi cần lại.

Ví dụ dưới đây cho thấy cách tôi ẩn sự kiện CellValueChanged của biểu đồ dữ liệu.

EventMask valueChangedEventMask; 

// In the class constructor 
valueChangedEventMask = new EventMask(
    () => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); }, 
    () => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); } 
); 

// Use push to hide the event and pop to make it available again. The operation can be nested or be used in the event itself. 
void changeCellOperation() 
{ 
    valueChangedEventMask.Push(); 

    ... 
    cell.Value = myNewCellValue 
    ... 

    valueChangedEventMask.Pop(); 
} 

// The class 
public class EventMask 
{ 
    Action hook; 
    Action unHook; 

    int count = 0; 

    public EventMask(Action hook, Action unHook) 
    { 
     this.hook = hook; 
     this.unHook = unHook; 
    } 

    public void Push() 
    { 
     count++; 
     if (count == 1) 
      unHook(); 
    } 

    public void Pop() 
    { 
     count--; 
     if (count == 0) 
      hook(); 
    } 
} 
Các vấn đề liên quan