เหตุการณ์ CheckedListBox ใดที่ทริกเกอร์หลังจากตรวจสอบรายการแล้ว


96

ฉันมี CheckedListBox ที่ฉันต้องการเหตุการณ์หลังจากตรวจสอบรายการเพื่อให้ฉันสามารถใช้ CheckedItems กับสถานะใหม่ได้

เนื่องจาก ItemChecked ถูกเริ่มทำงานก่อนที่จะมีการอัปเดต CheckedItems จึงไม่สามารถใช้งานได้นอกกรอบ

ฉันสามารถใช้วิธีการหรือเหตุการณ์ใดเพื่อรับการแจ้งเตือนเมื่อมีการอัปเดต CheckedItems

คำตอบ:


89

คุณสามารถใช้ItemCheckเหตุการณ์นี้ได้หากคุณตรวจสอบสถานะใหม่ของรายการที่กำลังคลิก นี้สามารถใช้ได้ใน args e.NewValueเหตุการณ์ที่เกิดขึ้นเป็น หากNewValueมีการตรวจสอบให้รวมรายการปัจจุบันพร้อมกับคอลเลกชันที่เหมาะสมในตรรกะของคุณ:

    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)
        {
            ...
        }
    }

เป็นอีกตัวอย่างหนึ่งเพื่อตรวจสอบว่าคอลเลกชันจะว่างเปล่าหลังจากเลือกรายการนี้ (ยกเลิก):

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
ในครั้งแรกสำหรับแต่ละข้อเราอาจต้องเพิ่ม if condition ..if not item = checkedListBox1.Items[e.Index].ToString()
Lenin Raj Rajasekaran

8
ปัญหาคือเหตุการณ์ ItemCheck จะเริ่มทำงานก่อนที่จะประมวลผลการตรวจสอบ การแก้ปัญหาของคุณจะเกี่ยวข้องกับการเก็บรักษารายการของคุณเองโดยพื้นฐานแล้วการทำซ้ำรหัสมาตรฐาน คำแนะนำแรกของ Dunc (การดำเนินการล่าช้าบน ItemCheck) เป็นคำตอบที่ชัดเจนที่สุดสำหรับคำถามของ phq เนื่องจากไม่ต้องการการจัดการเพิ่มเติมใด ๆ
Berend Engelbrecht

35

มีโพสต์ StackOverflow ที่เกี่ยวข้องมากมายในเรื่องนี้ ... เช่นเดียวกับวิธีแก้ปัญหาของ Branimir ต่อไปนี้เป็นวิธีง่ายๆอีกสองอย่าง:

การดำเนินการที่ล่าช้าใน ItemCheck (เช่นกันที่นี่ ):

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

การใช้งาน MouseUp :

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

ฉันชอบตัวเลือกแรกเนื่องจากตัวเลือกที่สองจะทำให้เกิดผลบวกที่ผิดพลาด (เช่นยิงบ่อยเกินไป)


13
วิธีที่สองจะพลาดรายการที่ถูกตรวจสอบหรือไม่เลือกผ่านแป้นพิมพ์

1
BeginInvoke เป็นสิ่งที่ฉันต้องการอย่างแท้จริงเนื่องจากเหตุการณ์ของฉันกำลังเรียกหาอินเทอร์เฟซซึ่งไม่รู้ว่าจะจัดการกับการควบคุมแบบใด คำตอบที่ยอมรับจะใช้ได้เฉพาะในกรณีในขณะที่ตรรกะสามารถดำเนินการได้ภายในตัวจัดการเหตุการณ์หรือสิ่งที่เรียกโดยตรงจากตัวจัดการเหตุการณ์ นี่ไม่ใช่กรณีสำหรับฉัน ขอบคุณสำหรับวิธีแก้ปัญหาที่ยอดเยี่ยม แต่เรียบง่ายนี้
Jesse

ขอบคุณตัวเลือกแรกกับ BeginInvoke ใช้ได้กับฉัน อาจจะเป็นคนแสดงความคิดเห็นโง่ ๆ .. แต่เหตุใด BUG นี้จึงรายงานในหัวข้อที่เริ่มในปี 2010 ไม่ได้รับการแก้ไขในปี 2018 ??
สารพัด

1
@ Goodies เห็นด้วยแม้ว่าฉันคิดว่ามันอาจทำลายรหัสจำนวนมากได้หาก Microsoft เปลี่ยนพฤติกรรมในตอนนี้ เอกสารThe check state is not updated until after the ItemCheck event occursอย่างชัดเจนรัฐ เหตุการณ์อื่นหรือวิธีแก้ปัญหาที่ไม่ได้กำหนดเองจะดีกว่า IMO
Dunc

24

ฉันลองแล้วและได้ผล:

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
นี้! ... น่าจะเป็นคำตอบที่ถูกต้องซึ่งน่าเสียดายที่สุด นี่เป็นการแฮ็กที่ไร้สาระซึ่งได้ผลเพราะมีคนที่ M $ ลืมที่จะใช้งานItemCheckedเหตุการณ์และไม่มีใครเคยพูดถึงว่ามันไม่มีอยู่จริง
RLH

แม้ว่าจะไม่ใช่ข้อผิดพลาดตามคำจำกัดความ แต่ฉันคิดว่าควรใช้สิ่งนี้หากคุณเห็นด้วยให้พิจารณาสนับสนุนรายงานข้อบกพร่องนี้โดยคลิกที่ +1: connect.microsoft.com/VisualStudio/feedback/details/1759293
SCBuergel.eth

@ เซบาสเตียน - ไม่ขอแก้ไขที่นี่ "การแก้ไข" ใด ๆ นี้จะทำให้การออกจากโซลูชันไม่สมบูรณ์ ถ้ามีสองเหตุการณ์: ItemChecking, ItemCheckedแล้วคุณสามารถใช้หนึ่งหลัง แต่ถ้ามีการใช้งานเพียงตัวเดียว ( ItemCheck) มันกำลังทำสิ่งต่าง ๆ อย่างถูกต้องนั่นคือเริ่มต้นเหตุการณ์ก่อนที่ค่าจะถูกตรวจสอบด้วยค่าใหม่และดัชนีที่ให้มาเป็นพารามิเตอร์ ใครต้องการเหตุการณ์ "หลังการเปลี่ยนแปลง" ก็สามารถใช้สิ่งที่กล่าวมาได้ หากแนะนำบางสิ่งบางอย่างกับ Microsoft แล้วแนะนำกิจกรรมใหม่ ItemCheckedโดยไม่เปลี่ยนเหตุการณ์ที่มีอยู่: ดูคำตอบของ
diimdeep

เช่นนี้ แต่ทางเลือกเล็กน้อยที่ฉันใช้ตลอดเวลาคือการตั้งค่าสถานะ "ข้าม" บางประเภทเพื่อให้ SetItemCheckState ไม่ทริกเกอร์เหตุการณ์เดียวกันอีกครั้ง ไม่ว่าจะเป็นทั่วโลกธรรมดา ๆ หรือสิ่งที่ฉันชอบทำคือตรวจสอบแท็ก ตัวอย่างเช่นตัดการดำเนินการใน If myCheckListBox.Tag! = null จากนั้นแทนที่ Event Delete \ Add เพียงแค่ตั้งค่าแท็กเป็นบางอย่าง (แม้แต่สตริงว่าง) จากนั้นกลับเป็น null เพื่อเปิดอีกครั้ง
da_jokker

10

มาจากCheckedListBoxและนำไปใช้

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

นั่นคือรหัสเพิ่มเติมมากมายที่คุณสามารถข้ามได้เมื่อใช้BeginInvokeวิธีแก้ปัญหาจากคำตอบที่สอง
Łukasƨ Fronczyk

4

แม้ว่าจะไม่เหมาะ แต่คุณสามารถคำนวณ CheckedItems โดยใช้อาร์กิวเมนต์ที่ส่งผ่านไปยังItemCheckเหตุการณ์ หากคุณดูตัวอย่างนี้ใน MSDNคุณสามารถระบุได้ว่ารายการที่เปลี่ยนแปลงใหม่ได้รับการตรวจสอบหรือไม่ได้ทำเครื่องหมายซึ่งจะทำให้คุณอยู่ในตำแหน่งที่เหมาะสมในการทำงานกับรายการ

คุณสามารถสร้างเหตุการณ์ใหม่ที่เริ่มทำงานหลังจากตรวจสอบรายการซึ่งจะให้สิ่งที่คุณต้องการหากคุณต้องการ


1
คุณมีความคิดที่เฉพาะเจาะจงเกี่ยวกับวิธีสร้างเหตุการณ์ใหม่นี้ฉันจะรู้ได้อย่างไรว่าเมื่อ CheckedItems ได้รับการอัปเดตหลังจากเหตุการณ์ ItemChecke
hultqvist

4

หลังจากการทดสอบบางครั้งฉันเห็นว่าเหตุการณ์ SelectedIndexChanged ถูกทริกเกอร์หลังจากเหตุการณ์ ItemCheck ให้คุณสมบัติ CheckOnClick True

การเข้ารหัสที่ดีที่สุด


คุณพูดถูกนี่เป็นวิธีที่ง่ายที่สุด แต่ก็ยังคงเป็นเหมือนการแฮ็กเนื่องจากเป็นพฤติกรรมที่ไม่มีเอกสารและไม่เกี่ยวข้อง น้องใหม่ที่ Microsoft อาจคิดว่า: อืมทำไมไฟ SelectedIndexChanged เมื่อ Checkstate เท่านั้นที่เปลี่ยนแปลง มาเพิ่มประสิทธิภาพกันเถอะ และปังก็ใช้รหัสของคุณ :(
Rolf

นอกจากนี้ SelectedIndexChanged ไม่เริ่มทำงานเมื่อคุณเปลี่ยนสถานะการตรวจสอบโดยทางโปรแกรม
Rolf

1
และจะไม่เริ่มทำงานเมื่อคุณเปลี่ยนสถานะการตรวจสอบด้วยปุ่ม Space การใช้สิ่งนี้ผิด
Elmue

2

งานนี้ไม่แน่ใจว่าจะสวยหรูขนาดไหน!

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

ไม่ทราบว่าใช้ได้หรือไม่ แต่ฉันต้องการใช้ช่องทำเครื่องหมายเพื่อกรองผลลัพธ์ ดังนั้นเมื่อผู้ใช้ตรวจสอบและไม่เลือกรายการฉันต้องการให้รายการแสดง \ ซ่อนรายการ

ฉันมีปัญหาบางอย่างที่ทำให้ฉันไปที่โพสต์นี้ แค่อยากจะแบ่งปันว่าฉันทำมันได้อย่างไรโดยไม่มีอะไรพิเศษ

หมายเหตุ: ฉันมีCheckOnClick = trueแต่อาจยังใช้งานได้หากไม่มี

เหตุการณ์ที่ฉันใช้คือ " SelectedIndexChanged "

การแจงนับที่ฉันใช้คือ " .CheckedItems "

สิ่งนี้ให้ผลลัพธ์ที่ฉันคิดว่าเราอาจคาดหวัง ทำให้ง่ายขึ้นจึงลงมาที่ ....

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 ไม่เริ่มทำงานเมื่อผู้ใช้เปลี่ยนสถานะการตรวจสอบด้วยปุ่ม Space
Elmue

SelectedIndexChanged ไม่เริ่มทำงานเมื่อเรียก SetItemChecked เพื่อตรวจสอบหรือยกเลิกการเลือกรายการในรหัส
bkqc

1

สมมติว่าคุณต้องการเก็บอาร์กิวเมนต์ไว้ItemCheckแต่ได้รับการแจ้งเตือนหลังจากเปลี่ยนโมเดลแล้วควรมีลักษณะดังนี้:

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

CheckedItemsChangedจะอยู่ที่ไหน:

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

0

ฉันลองแล้วและได้ผล:

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

กำหนดโดยดัชนีของรายการ


-1

ฉันใช้ตัวตั้งเวลาเพื่อแก้ปัญหานี้ เปิดใช้งานตัวจับเวลาผ่านเหตุการณ์ ItemCheck ดำเนินการในกิจกรรม Timer's Tick

วิธีนี้ใช้งานได้ไม่ว่าจะตรวจสอบรายการผ่านการคลิกเมาส์หรือโดยการกด Space-Bar เราจะใช้ประโยชน์จากข้อเท็จจริงที่ว่ารายการที่เพิ่งตรวจสอบ (หรือยกเลิกการเลือก) นั้นเป็นรายการที่เลือกเสมอ

ช่วงเวลาของตัวจับเวลาสามารถต่ำได้ถึง 1 เมื่อถึงเวลาที่เหตุการณ์ Tick ขึ้นสถานะการตรวจสอบใหม่จะถูกตั้งค่า

รหัส VB.NET นี้แสดงแนวคิด มีหลายรูปแบบที่คุณสามารถใช้ คุณอาจต้องการเพิ่มช่วงเวลาของตัวจับเวลาเพื่อให้ผู้ใช้เปลี่ยนสถานะการตรวจสอบในหลาย ๆ รายการก่อนที่จะดำเนินการ จากนั้นในเหตุการณ์ Tick ให้สร้างรายการตามลำดับของรายการทั้งหมดในรายการหรือใช้คอลเล็กชัน CheckedItems เพื่อดำเนินการที่เหมาะสม

นั่นเป็นเหตุผลที่เราปิดตัวจับเวลาในเหตุการณ์ ItemCheck ก่อน ปิดการใช้งานจากนั้นเปิดใช้งานทำให้ช่วงเวลา Interval เริ่มต้นใหม่

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

1
ขอขอบคุณสำหรับการแบ่งปัน ในทางกลับกันบางทีคุณอาจเรียนรู้วิธีแก้ปัญหาที่ดีกว่าจากคำตอบอื่น ๆ การใช้ Timer ค่อนข้างซับซ้อนและในกรณีนี้เป็นเครื่องมือที่ไม่ถูกต้องสำหรับงานเนื่องจากคุณได้รับค่าใหม่เป็นพารามิเตอร์แล้ว ดังนั้นคุณสามารถใช้คำตอบนี้สำหรับการแก้ปัญหาครั้งเดียวหรือคำตอบนี้สำหรับการแก้ปัญหาอย่างเป็นระบบ แปลงไฟล์จาก C # เป็น VB โดยใช้หนึ่งในเครื่องมือแปลงออนไลน์
miroxlav

-1

ในลักษณะการทำงานปกติเมื่อเราตรวจสอบรายการหนึ่งสถานะการตรวจสอบของรายการจะเปลี่ยนไปก่อนที่ตัวจัดการเหตุการณ์จะถูกยกขึ้น แต่ CheckListBox มีลักษณะการทำงานที่แตกต่างกัน: ตัวจัดการเหตุการณ์จะถูกยกขึ้นก่อนที่สถานะการตรวจสอบของรายการจะเปลี่ยนแปลงและทำให้แก้ไขงานของเราได้ยาก

ในความคิดของฉันในการแก้ปัญหานี้เราควรเลื่อนตัวจัดการเหตุการณ์ออกไป

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());}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.