Sự kiện CheckedListBox nào kích hoạt sau khi một mục được chọn?


94

Tôi có 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.

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

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?

Câu trả lời:


87

Bạn có thể sử dụng ItemChecksự kiện, 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 các args sự kiện, như e.NewValue. Nếu NewValueđược chọn, hãy bao gồm mục hiện tại cùng với bộ sưu tập phù hợp theo 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());
        else
            checkedItems.Remove(checkedListBox1.Items[e.Index].ToString());

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

Như một ví dụ khác, để xác định xem bộ sưu tập có trống không sau khi mục này được (bỏ-) chọn:

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 phần đầu tiên cho mỗi điều kiện, chúng ta có thể cần thêm một điều kiện nếu ..if not item = checkedListBox1.Items[e.Index].ToString()
Lenin Raj Rajasekaran

8
Vấn đề là sự kiện ItemCheck được kích hoạt trước khi quá trình 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 là sao chép mã chuẩn. Đề xuất đầu tiên của Dunc (Trì hoãn thực thi trên ItemCheck) là imo câu trả lời rõ ràng nhất cho câu hỏi về phq, vì nó không yêu cầu bất kỳ xử lý bổ sung nào.
Berend Engelbrecht

34

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

Thực thi bị trì hoãn trên ItemCheck (cũng ở đây ):

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

Sử dụng sự kiện MouseUp :

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

Tôi thích tùy chọn đầu tiên hơ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).


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

1
BeginInvoke chính là thứ tôi cần vì sự kiện của tôi thực sự gọi ra một giao diện, không biết nó đang xử lý loại điều khiển nào. 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 thứ 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

Thx, tùy chọn đầu tiên với BeginInvoke phù hợp với tôi. Có thể là một bình luận ngớ ngẩn mọi người .. nhưng tại sao BUG này được báo cáo trong một chủ đề bắt đầu từ năm 2010 không được giải quyết vào năm 2018 ??
Goodies

1
@Goodies Đồng ý, mặc dù tôi đoán nó có thể phá vỡ nhiều mã nếu Microsoft thay đổi hành vi ngay bây giờ. Tài liệu tuyên bố rõ ràng The check state is not updated until after the ItemCheck event occurs. Một sự kiện khác hoặc cách giải quyết không tùy ý sẽ là IMO tốt.
Dunc

24

Tôi đã thử điều này và nó hoạt động:

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();        
}

8
Điều này! ... phải là câu trả lời chính xác, đó là điều đáng tiếc nhất. Đây là một vụ hack vô lý hoạt động bởi vì ai đó tại M $ đã quên thực hiện ItemCheckedsự kiện và không ai nói rằng nó không tồn tại.
RLH

Mặc dù nó không phải là theo định nghĩa một lỗi Tôi nghĩ rằng điều này sẽ đượ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: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@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, sau đó bạn có thể sử dụng một thứ hai. Nhưng nếu chỉ có một được thực thi ( ItemCheck) thì nó đang làm những việc chính xác, tức là kích hoạt sự kiện trước khi giá trị được kiểm tra với giá trị mới và chỉ mục được cung cấp dưới dạng tham số. Ai muốn sự kiện "sau khi thay đổi", họ có thể chỉ cần sử dụng ở trên. Nếu đề nghị một cái gì đó để Microsoft sau đó đề nghị một sự kiện mới ItemChecked , không thay đổi của một hiện: xem diimdeep của câu trả lời
miroxlav

Giống như điều này, nhưng một giải pháp thay thế nhỏ mà tôi sử dụng mọi lúc chỉ là đặt một số loại cờ "bỏ qua" để SetItemCheckState không kích hoạt lại cùng một sự kiện. Một toàn cầu đơn giản hoặc những gì tôi muốn làm là đảm bảo thẻ. ví dụ: bọc hành động trong If myCheckListBox.Tag! = null, sau đó thay cho Event Delete \ Add, chỉ cần đặt thẻ thành một cái gì đó (ngay cả một chuỗi trống) và sau đó quay lại null để bật lại.
da_jokker

10

Bắt nguồn từ CheckedListBoxvà 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

Mặc dù không lý tưởng, bạn có thể tính toán CheckedItems bằng cách sử dụng các đối số được chuyển cho ItemChecksự kiện. Nếu bạn xem ví dụ này trên MSDN , bạn có thể xác định xem mục mới thay đổi đã được chọn hay chưa được chọn, điều này giúp bạn có 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 một 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ề cách tạo sự kiện mới này không, làm cách nào để biết được CheckedItems đã được cập nhật sau sự kiện ItemChecke?
hultqvist

4

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

Mã hóa tốt nhất


Bạn nói đúng, đây là cách dễ nhất. Nhưng nó vẫn là một cái gì đó giống như một cuộc tấn công, bởi vì nó là hành vi không có giấy tờ và không được PHÁT HIỆN. Bất kỳ sinh viên năm nhất nào tại Microsoft cũng có thể nghĩ: ồ ồ, tại sao lại kích hoạt SelectedIndexChanged khi chỉ có Checkstate thay đổi. Hãy tối ưu hóa điều đó. Và Bang gửi mã của bạn :(
Rolf

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

1
Và nó không kích hoạt khi bạn thay đổi trạng thái kiểm tra bằng phím Space. Nó là sai khi sử dụng cái này.
Elmue

2

Điều này hoạt động, nhưng không chắc nó thanh lịch như thế nào!

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

1

Không biết điều này có áp dụng không nhưng tôi muốn sử dụng một hộp kiểm để 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ố vấn đề dẫn đế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ó thể vẫn hoạt động nếu không có

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

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

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

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
        }
   }

}

SelectedIndexChanged không kích hoạt khi người dùng thay đổi trạng thái kiểm tra bằng phím Space.
Elmue

SelectedIndexChanged không kích hoạt khi callin SetItemChecked để kiểm tra hoặc bỏ chọn một mục trong mã.
bkqc

1

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

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

CheckedItemsChangedCó thể ở đâu :

private void CheckedItemsChanged(object sender, EventArgs e)
{
    DoYourThing();
}

0

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

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

Khoảng thời gian của Bộ hẹn giờ có thể thấp đến 1. Vào thời điểm sự kiện Đánh dấu được nâng lên, trạng thái Đã kiểm tra mới sẽ được thiết lập.

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 bộ hẹn giờ để 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 Đánh dấu, hãy thực hiện tuần tự 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ó để thực hiện hành động thích hợp.

Đó là lý do tại sao trước tiên chúng tôi vô hiệu hóa Bộ hẹn giờ trong sự kiện ItemCheck. Tắt rồi Bật làm cho 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

Cảm ơn bạn đã chia sẻ. Mặt khác, có lẽ bạn có thể tìm hiểu các giải pháp tốt hơn từ các câu trả lời khác. Việc sử dụng Bộ hẹn giờ tương đối phức tạp và trong trường hợp này, nó 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 dưới dạng tham số. Vì vậy, bạn có thể sử dụng câu trả lời này cho giải pháp một lần hoặc câu trả lời này cho giải pháp hệ thống. Chuyển đổi chúng từ C # sang VB bằng một trong những công cụ chuyển đổi trực tuyến.
miroxlav

0

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

Theo tôi, để giải quyết vấn đề này, chúng ta nên trì hoãn trình 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());}

0

Tôi đã thử điều này và nó hoạt động:

    private List<bool> m_list = new List<bool>();
    private void Initialize()
    {
        for(int i=0; i < checkedListBox1.Items.Count; i++)
        {
            m_list.Add(false);
        }
    }

    private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
    {
        if (e.NewValue == CheckState.Checked)
        {
            m_list[e.Index] = true;
            checkedListBox1.SetItemChecked(e.Index, true);
        }
        else
        {
            m_list[e.Index] = false;
            checkedListBox1.SetItemChecked(e.Index, false);
        }
    }

xác định theo chỉ số của danh sách.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.