2010-09-08 26 views
65

Tôi có một CheckedListBox nơi tôi muốn một sự kiện sau khi một mục được chọn để tôi có thể sử dụng CheckedItems với trạng thái mới.Kích hoạt sự kiện CheckedListBox nào sau khi một mục được chọn?

Vì ItemChecked được kích hoạt trước khi CheckedItems được cập nhật, nó sẽ không hoạt động.

Tôi có thể sử dụng loại phương pháp hoặc sự kiện nào để được thông báo khi CheckedItems được cập nhật?

Trả lời

55

Bạn có thể sử dụng sự kiện ItemCheck, nếu bạn cũng kiểm tra trạng thái mới của mục đang được nhấp. Điều này có sẵn trong sự kiện arg, là e.NewValue. Nếu NewValue được kiểm tra, bao gồm các mục hiện hành cùng với bộ sưu tập thích hợp trong logic của bạn:

private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) 
    {      
     List<string> checkedItems = new List<string>(); 
     foreach (var item in checkedListBox1.CheckedItems) 
      checkedItems.Add(item.ToString()); 

     if (e.NewValue == CheckState.Checked) 
      checkedItems.Add(checkedListBox1.Items[e.Index].ToString()); 

     foreach (string item in checkedItems) 
     { 
      ... 
     } 
    } 

Một ví dụ khác, để xác định xem bộ sưu tập sẽ được sản phẩm nào sau khi mặt hàng này (un-) kiểm tra:

private void ListProjects_ItemCheck(object sender, ItemCheckEventArgs args) 
{ 
    if (ListProjects.CheckedItems.Count == 1 && args.NewValue == CheckState.Unchecked) 
     // The collection is about to be emptied: there's just one item checked, and it's being unchecked at this moment 
     ... 
    else 
     // The collection will not be empty once this click is handled 
     ... 
} 
+3

trong đầu tiên cho mỗi, chúng ta có thể cần phải thêm một nếu điều kiện .. 'if not item = checkedListBox1.Items [e.Index]. ToString()' – emaillenin

+8

Vấn đề là sự kiện ItemCheck là f ired trước khi kiểm tra được xử lý. Giải pháp của bạn sẽ liên quan đến việc giữ danh sách của riêng bạn, về cơ bản nhân đôi mã tiêu chuẩn. Đề xuất đầu tiên của Dunc (thực thi trì hoãn trên ItemCheck) là imo câu trả lời rõ ràng nhất cho câu hỏi của phq, bởi vì nó không yêu cầu bất kỳ xử lý bổ sung nào. –

4

Mặc dù không lý tưởng, bạn có thể tính toán các mục CheckedItems bằng các đối số được chuyển đến sự kiện ItemCheck. Nếu bạn nhìn vào điều này example on MSDN, bạn có thể làm việc ra cho dù các mục mới được thay đổi đã được kiểm tra hoặc bỏ chọn, mà lá bạn ở một vị trí thích hợp để làm việc với các mục.

Bạn thậm chí có thể tạo sự kiện mới kích hoạt sau khi một mục được chọn, điều này sẽ cung cấp cho bạn chính xác những gì bạn muốn nếu bạn muốn.

+1

Bạn có ý tưởng cụ thể nào về sự kiện mới này có thể được tạo ra không, làm thế nào tôi có thể biết khi nào CheckedItems đã được cập nhật sau sự kiện ItemChecke? – hultqvist

22

Có rất nhiều bài viết StackOverflow liên quan về vấn đề này ... Cũng như Branimir's giải pháp, ở đây được hơn hai cái đơn giản:

Delayed execution on ItemCheck (còn here):

void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) 
    { 
     this.BeginInvoke((MethodInvoker) (
      () => Console.WriteLine(checkedListBox1.SelectedItems.Count))); 
    } 

Using the MouseUp event:

void checkedListBox1_MouseUp(object sender, MouseEventArgs e) 
    { 
     Console.WriteLine(checkedListBox1.SelectedItems.Count); 
    } 

Tôi thích tùy chọn đầu tiên, vì tùy chọn thứ hai sẽ dẫn đến kết quả dương tính giả (tức là kích hoạt quá thường xuyên).

+8

Phương pháp thứ hai cũng sẽ bỏ lỡ các mục được chọn hoặc bỏ chọn thông qua bàn phím. –

+1

BeginInvoke chính xác là những gì tôi cần vì sự kiện của tôi thực sự đang gọi ra một giao diện, điều đó không có ý thức về loại điều khiển mà nó đang xử lý. Câu trả lời được chấp nhận chỉ hoạt động trong các trường hợp trong khi logic có thể được thực hiện trong trình xử lý sự kiện, hoặc một cái gì đó được gọi trực tiếp từ trình xử lý sự kiện. Đây không phải là trường hợp của tôi. Cảm ơn vì giải pháp tuyệt vời nhưng đơn giản này. – Jesse

2

Công trình này, không chắc chắn về mức độ thanh lịch của nó!

Private Sub chkFilters_Changed(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkFilters.ItemCheck 
    Static Updating As Boolean 
    If Updating Then Exit Sub 
    Updating = True 

    Dim cmbBox As CheckedListBox = sender 
    Dim Item As ItemCheckEventArgs = e 

    If Item.NewValue = CheckState.Checked Then 
     cmbBox.SetItemChecked(Item.Index, True) 
    Else 
     cmbBox.SetItemChecked(Item.Index, False) 
    End If 

    'Do something with the updated checked box 
    Call LoadListData(Me, False) 

    Updating = False 
End Sub 
15

Tôi cố gắng này và nó làm việc:

private void clbOrg_ItemCheck(object sender, ItemCheckEventArgs e) 
{ 
    CheckedListBox clb = (CheckedListBox)sender; 
    // Switch off event handler 
    clb.ItemCheck -= clbOrg_ItemCheck; 
    clb.SetItemCheckState(e.Index, e.NewValue); 
    // Switch on event handler 
    clb.ItemCheck += clbOrg_ItemCheck; 

    // Now you can go further 
    CallExternalRoutine();   
} 
+5

Điều này! ... nên là câu trả lời đúng, điều đáng tiếc nhất. Đây là một hack vô lý mà làm việc vì một người nào đó tại M $ quên để thực hiện sự kiện 'ItemChecked', và không ai từng nói rằng nó không tồn tại. – RLH

+0

Mặc dù nó không phải là định nghĩa một lỗi Tôi nghĩ rằng điều này nên được thực hiện, nếu bạn đồng ý xem xét hỗ trợ báo cáo lỗi này bằng cách nhấp vào +1: https://connect.microsoft.com/VisualStudio/feedback/details/1759293 – Sebastian

+0

@Sebastian - không yêu cầu sửa chữa ở đây. Bất kỳ "sửa chữa" này sẽ phá vỡ các giải pháp thoát. Nếu có hai sự kiện: 'ItemChecking',' ItemChecked', thì bạn có thể sử dụng thứ hai. Nhưng nếu chỉ có một được thực hiện ('ItemCheck') nó đang làm những việc chính xác, tức là bắn sự kiện * trước khi * giá trị được kiểm tra với giá trị và chỉ số mới được cung cấp dưới dạng tham số. Bất cứ ai muốn "sau khi thay đổi" sự kiện, họ chỉ có thể sử dụng ở trên. Nếu đề nghị một cái gì đó cho Microsoft sau đó đề nghị một ** sự kiện mới ** 'ItemChecked', không thay đổi một hiện tại: xem [câu trả lời của diimdeep] (http://stackoverflow.com/a/18487539/2392157) – miroxlav

8

xuất phát từ CheckedListBox và thực hiện

/// <summary> 
/// Raises the <see cref="E:System.Windows.Forms.CheckedListBox.ItemCheck"/> event. 
/// </summary> 
/// <param name="ice">An <see cref="T:System.Windows.Forms.ItemCheckEventArgs"/> that contains the event data. 
///     </param> 
protected override void OnItemCheck(ItemCheckEventArgs e) 
{   
    base.OnItemCheck(e); 

    EventHandler handler = AfterItemCheck; 
    if (handler != null) 
    { 
     Delegate[] invocationList = AfterItemCheck.GetInvocationList(); 
     foreach (var receiver in invocationList) 
     { 
      AfterItemCheck -= (EventHandler) receiver; 
     } 

     SetItemCheckState(e.Index, e.NewValue); 

     foreach (var receiver in invocationList) 
     { 
      AfterItemCheck += (EventHandler) receiver; 
     } 
    } 
    OnAfterItemCheck(EventArgs.Empty); 
} 

public event EventHandler AfterItemCheck; 

public void OnAfterItemCheck(EventArgs e) 
{ 
    EventHandler handler = AfterItemCheck; 
    if (handler != null) 
     handler(this, e); 
} 
4

Sau khi một số xét nghiệm, tôi có thể thấy rằng sự kiện SelectedIndexChanged được kích hoạt sau khi sự kiện ItemCheck . Giữ tài sản CheckOnClick True

Mã hóa tốt nhất

+0

Bạn nói đúng, đây là cách dễ nhất. Nhưng nó vẫn giống như một hack, bởi vì nó là hành vi không có giấy tờ và không được khai báo. Bất kỳ sinh viên năm nhất tại Microsoft có thể nghĩ rằng: oh tốt, tại sao lửa SelectedIndexChanged khi chỉ thay đổi Checkstate. Hãy tối ưu hóa điều đó. Và Bang đi mã của bạn: ( – Rolf

+0

Ngoài ra, SelectedIndexChanged không kích hoạt khi bạn thay đổi trạng thái kiểm tra theo lập trình – Rolf

-1

Tôi sử dụng Bộ hẹn giờ để giải quyết vấn đề này. Bật bộ hẹn giờ thông qua sự kiện ItemCheck. Thực hiện hành động trong sự kiện Tick của Timer.

Điều này hoạt động cho dù mục được kiểm tra qua nhấp chuột hay bằng cách nhấn Space-Bar.Chúng tôi sẽ tận dụng lợi thế của thực tế là mục vừa kiểm tra (hoặc bỏ chọn) luôn là mục được chọn.

Khoảng thời gian của người lập lịch có thể thấp nhất 1. Khi thời gian sự kiện Tick được nâng lên, trạng thái Đã kiểm tra mới sẽ được đặt.

Mã VB.NET này hiển thị khái niệm. Có nhiều biến thể bạn có thể sử dụng. Bạn có thể muốn tăng Khoảng thời gian của Timer để cho phép người dùng thay đổi trạng thái kiểm tra trên một số mục trước khi thực hiện hành động. Sau đó, trong sự kiện Tick, thực hiện một pass tuần tự của tất cả các mục trong danh sách hoặc sử dụng bộ sưu tập CheckedItems của nó để có hành động thích hợp.

Đó là lý do tại sao trước tiên chúng tôi tắt Bộ hẹn giờ trong sự kiện ItemCheck. Tắt rồi bật Bật sẽ làm cho khoảng thời gian Khoảng thời gian bắt đầu lại.

Private Sub ckl_ItemCheck(ByVal sender As Object, _ 
          ByVal e As System.Windows.Forms.ItemCheckEventArgs) _ 
    Handles ckl.ItemCheck 

tmr.Enabled = False 
tmr.Enabled = True 

End Sub 


Private Sub tmr_Tick(ByVal sender As System.Object, _ 
        ByVal e As System.EventArgs) _ 
    Handles tmr.Tick 

tmr.Enabled = False 
Debug.Write(ckl.SelectedIndex) 
Debug.Write(": ") 
Debug.WriteLine(ckl.GetItemChecked(ckl.SelectedIndex).ToString) 

End Sub 
+0

Bạn có thể chia sẻ. và trong trường hợp này đó là công cụ sai cho công việc, bởi vì bạn thực sự đã nhận được các giá trị mới như các tham số. Vì vậy, bạn có thể sử dụng [câu trả lời này] (http://stackoverflow.com/a/17511730/2392157) cho một- tắt giải pháp hoặc [this one] (http://stackoverflow.com/a/18487539/2392157) cho giải pháp có hệ thống, chuyển đổi chúng từ C# sang VB bằng một trong các công cụ chuyển đổi trực tuyến. – miroxlav

1

Không biết điều này có áp dụng hay không nhưng tôi muốn sử dụng hộp kiểm tra để lọc kết quả. Vì vậy, khi người dùng đã chọn và bỏ chọn các mục tôi muốn danh sách hiển thị \ ẩn các mục.

Tôi đã gặp một số sự cố dẫn tôi đến bài đăng này. Chỉ muốn chia sẻ cách tôi đã làm nó mà không có gì đặc biệt.

Lưu ý: Tôi có CheckOnClick = true nhưng nó có lẽ sẽ vẫn làm việc mà không

Sự kiện này tôi sử dụng là "SelectedIndexChanged"

kiểu liệt kê tôi sử dụng là ".CheckedItems"

Điều này cho kết quả tôi nghĩ chúng tôi có thể mong đợi. Vì vậy, đơn giản hóa nó đi xuống đến ....

private void clb1_SelectedIndexChanged(object sender, EventArgs e) 
{ 
    // This just spits out what is selected for testing 
    foreach (string strChoice in clb1.CheckedItems) 
    { 
     listBox1.Items.Add(strChoice); 
    } 

    //Something more like what I'm actually doing 
    foreach (object myRecord in myRecords) 
    { 
     if (clb1.CheckItems.Contains(myRecord["fieldname"]) 
     { 
      //Display this record 
     } 
    } 

} 
0

Trong hành vi bình thường, khi chúng tôi kiểm tra một mục, tiểu bang kiểm tra mục sẽ thay đổi trước khi xử lý sự kiện tăng. Nhưng CheckListBox hoạt động với các hành vi khác nhau: Trình xử lý sự kiện tăng trước khi kiểm tra trạng thái thay đổi mục và làm cho việc sửa lỗi của chúng tôi trở nên khó khăn.

Trong ý kiến ​​của tôi, để giải quyết vấn đề này, chúng ta nên hoãn xử lý sự kiện.

private void _clb_ItemCheck(object sender, ItemCheckEventArgs e) { 
// Defer event handler execution 
Task.Factory.StartNew(() => { 
    Thread.Sleep(1000); 
    // Do your job at here 
}) 
.ContinueWith(t => { 
    // Then update GUI at here 
},TaskScheduler.FromCurrentSynchronizationContext());} 
1

Giả sử bạn muốn giữ các đối số từ ItemCheck nhưng nhận được thông báo sau khi mô hình đã được thay đổi nó sẽ giống như thế:

CheckedListBox ctrl = new CheckedListBox(); 
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e))); 

đâu CheckedItemsChanged có thể là:

private void CheckedItemsChanged(object sender, EventArgs e) 
{ 
    DoYourThing(); 
} 
Các vấn đề liên quan