จะตรวจจับการเปลี่ยนแปลงเหตุการณ์ DataGridView CheckBox ได้อย่างไร?


92

ฉันมีแอป winforms และต้องการเรียกใช้โค้ดบางอย่างเมื่อมีDataGridViewการเลือก / ไม่เลือกช่องทำเครื่องหมายที่ฝังอยู่ในการควบคุม ทุกเหตุการณ์ฉันพยายามอย่างใดอย่างหนึ่ง

  1. ทริกเกอร์ทันทีที่CheckBoxคลิก แต่ก่อนที่สถานะการตรวจสอบจะเปลี่ยนไปหรือ
  2. ทริกเกอร์เพียงครั้งเดียวที่CheckBoxสูญเสียโฟกัส

ฉันไม่พบเหตุการณ์ที่ทริกเกอร์ทันทีหลังจากที่สถานะการตรวจสอบเปลี่ยนไป


แก้ไข:

สิ่งที่ฉันพยายามจะบรรลุคือเมื่อสถานะการตรวจสอบของการเปลี่ยนแปลงCheckBoxในหนึ่งเดียวDataGridViewข้อมูลในอีกสองรายการDataGridViewจะเปลี่ยนไป เหตุการณ์ทั้งหมดที่ฉันใช้ข้อมูลในกริดอื่น ๆ จะเปลี่ยนไปหลังจากที่CheckBoxในครั้งแรกDataGridViewสูญเสียโฟกัสเท่านั้น


2
คุณตรวจสอบCurrentCellDirtyStateChangedเหตุการณ์หรือไม่
Yograj Gupta

ยังคงดำเนินการต่อเมื่อผู้ใช้ 'ออกจากเซลล์'
PJW

1
นี่คือบทความ MSDN เกี่ยวกับเรื่องนี้: msdn.microsoft.com/en-us/library/…คล้ายกัน แต่แตกต่างกันเล็กน้อยกับคำตอบของ Killercam
David Hall

คำตอบ:


98

ในการจัดการDatGridViewของCheckedChangedเหตุการณ์ที่คุณต้องได้รับCellContentClickไฟ (ซึ่งไม่ได้มีCheckBoxES สถานะปัจจุบัน!) CommitEditแล้วโทร สิ่งนี้จะทำให้CellValueChangedเหตุการณ์ที่คุณสามารถใช้ทำงานของคุณได้ นี่คือการกำกับดูแลโดยไมโครซอฟท์ ทำสิ่งต่อไปนี้ ...

private void dataGridViewSites_CellContentClick(object sender, 
    DataGridViewCellEventArgs e)
{
    dataGridViewSites.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

/// <summary>
/// Works with the above.
/// </summary>
private void dataGridViewSites_CellValueChanged(object sender, 
    DataGridViewCellEventArgs e)
{
    UpdateDataGridViewSite();
}

ฉันหวังว่านี่จะช่วยได้.

ปล. ตรวจสอบบทความนี้https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx


5
นี่เป็นวิธีแก้ปัญหาที่ดี แต่ไม่ได้ผลหากผู้ใช้คลิกหลายครั้งมีทางเลือกอื่นโพสต์ไว้ด้านล่างstackoverflow.com/questions/11843488/…
56ka

1
ฉันขอแนะนำอย่างยิ่งว่าอย่าใช้วิธีนี้กับปัญหาการดับเบิลคลิก จำเป็นต้องเรียกใช้ฟังก์ชัน EndEdit () ... ค้นหาลิงก์จาก @ 56ka แล้วคลิกลิงก์บทความ!
ลุค

1
ฉันใช้เวลาไม่นานในการแก้ปัญหานี้และถ้าโซลูชันของ @ 56ka เป็นวิธีที่ดีกว่าก็ยอดเยี่ยม อย่างไรก็ตามฉันไม่แน่ใจว่าความยุ่งยากทั้งหมดเกี่ยวกับการดับเบิลคลิกDataGridViewCheckBoxคืออะไร นี่ไม่ใช่ WPF และการดับเบิลคลิกที่ตัวควบคุมจะไม่ทำลายการผูกข้อมูลใด ๆ แต่เป็น WinForms การคลิกสองครั้งอาจไม่อัปเดตการควบคุมด้วยภาพ แต่ก็ไม่ได้ทำลายอะไรและในกรณีนี้วิธีแก้ปัญหาด้านล่างอาจเป็นวิธีที่ดีกว่า ขอบคุณ.
MoonKnight

สิ่งนี้จะทำงานได้อย่างสมบูรณ์หากคุณเพิ่มรหัสเดียวกันจากCellContentClickเข้าไปCellContentDoubleClickด้วย CellMouseUpจะเริ่มทำงานแม้ว่าเซลล์จะถูกเลือกไว้ แต่ไม่ได้คลิกช่องทำเครื่องหมาย - ซึ่งไม่ใช่พฤติกรรมที่ต้องการ
เหยื่อที่น่ากลัว

89

ฉันพบว่าโซลูชันของ @ Killercam ใช้งานได้ แต่ค่อนข้างหลบถ้าผู้ใช้ดับเบิลคลิกเร็วเกินไป ไม่แน่ใจว่าผู้อื่นพบว่ากรณีนี้หรือไม่ ผมพบว่าวิธีการแก้ปัญหาอื่นที่นี่

ใช้ดาต้ากริดCellValueChangedและCellMouseUp. ฉางหงอธิบายว่า

"สาเหตุที่ทำให้เหตุการณ์ OnCellvalueChanged ไม่เริ่มทำงานจนกว่า DataGridView จะคิดว่าคุณแก้ไขเสร็จแล้วสิ่งนี้ทำให้รู้สึกถึงคอลัมน์กล่องข้อความเนื่องจาก OnCellvalueChanged จะไม่ [รบกวน] ในการเริ่มการทำงานสำหรับการประท้วงแต่ละครั้ง แต่จะไม่ [ เหมาะสม] สำหรับกล่องกาเครื่องหมาย "

นี่คือการดำเนินการจากตัวอย่างของเขา:

private void myDataGrid_OnCellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
    {
        // Handle checkbox state change here
    }
}

และรหัสเพื่อบอกช่องทำเครื่องหมายว่าแก้ไขเสร็จแล้วเมื่อคลิกแทนที่จะรอจนกว่าผู้ใช้จะออกจากฟิลด์:

private void myDataGrid_OnCellMouseUp(object sender,DataGridViewCellMouseEventArgs e)
{
    // End of edition on each click on column of checkbox
    if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
    {
        myDataGrid.EndEdit();
    }
}

แก้ไข: เหตุการณ์ DoubleClick จะถือว่าแยกจากเหตุการณ์ MouseUp หากตรวจพบเหตุการณ์ DoubleClick แอปพลิเคชันจะละเว้นเหตุการณ์ MouseUp แรกโดยสิ้นเชิง จำเป็นต้องเพิ่มตรรกะนี้ในเหตุการณ์ CellDoubleClick นอกเหนือจากเหตุการณ์ MouseUp:

private void myDataGrid_OnCellDoubleClick(object sender,DataGridViewCellEventArgs e)
{
    // End of edition on each click on column of checkbox
    if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
    {
        myDataGrid.EndEdit();
    }
}

3
ฉันพบปัญหาการดับเบิลคลิกที่ผู้ตอบกลับสังเกตเห็นและปัญหานี้ทำงานได้ดีกว่าวิธีแก้ปัญหาแรกในการจัดการอย่างถูกต้อง
Steve Ferguson

1
ฉันยังพบปัญหาดับเบิลคลิกและวิธีนี้แก้ไขได้
Chris C

คลิกปุ่ม "ที่นี่" และดูบทความ ฉันมีปัญหาเดียวกันกับการดับเบิลคลิก
ลุค

4
จะเกิดอะไรขึ้นถ้าคุณสลับการสลับด้วยแถบเว้นวรรค
Halfgaar

1
การ 'แก้ไข' ปัญหาสเปซบาร์ผมตั้งKeyPreviewให้เป็นจริงในรูปแบบและเมื่อชุดe.KeyCode == Keys.Space e.Handled = trueกล่าวอีกนัยหนึ่งฉันเพิ่งปิดใช้งานการแก้ไขแป้นพิมพ์
Halfgaar

9

โซลูชันของ jsturtevants ทำงานได้ดี อย่างไรก็ตามฉันเลือกที่จะทำการประมวลผลในเหตุการณ์ EndEdit ฉันชอบแนวทางนี้ (ในแอปพลิเคชันของฉัน) เพราะไม่เหมือนกับเหตุการณ์ CellValueChanged เหตุการณ์ EndEdit จะไม่เริ่มทำงานในขณะที่คุณกำลังเติมข้อมูลในตาราง

นี่คือรหัสของฉัน (ส่วนหนึ่งถูกขโมยจาก jsturtevant:

private void gridCategories_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
    {
        //do some stuff
    }
}



private void gridCategories_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
    {
        gridCategories.EndEdit();
    }
}

3
คำตอบที่ดี แต่ควรใช้CellContentClickแทนCellMouseUpเนื่องจากคำตอบจะถูกเรียกเมื่อผู้ใช้คลิกที่ใดก็ได้ภายในเซลล์ในขณะที่คำตอบเดิมจะถูกเรียกเมื่อคลิกช่องทำเครื่องหมายเท่านั้น
Jamie Kitson

6

นอกจากนี้ยังจัดการกับการเปิดใช้งานแป้นพิมพ์

    private void dgvApps_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        if(dgvApps.CurrentCell.GetType() == typeof(DataGridViewCheckBoxCell))
        {
            if (dgvApps.CurrentCell.IsInEditMode)
            {
                if (dgvApps.IsCurrentCellDirty)
                {
                    dgvApps.EndEdit();
                }
            }
        }
    }


    private void dgvApps_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
          // handle value changed.....
    }

5

ตาม Killercam'answer รหัสของฉัน

private void dgvProducts_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        dgvProducts.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

และ:

private void dgvProducts_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        if (dgvProducts.DataSource != null)
        {
            if (dgvProducts.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString() == "True")
            {
                //do something
            }
            else
            {
               //do something
            }
        }
    }

5

นี่คือรหัสบางส่วน:

private void dgvStandingOrder_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    if (dgvStandingOrder.Columns[e.ColumnIndex].Name == "IsSelected" && dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
    {
        bool isChecked = (bool)dgvStandingOrder[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
        if (isChecked == false)
        {
            dgvStandingOrder.Rows[e.RowIndex].Cells["Status"].Value = "";
        }
        dgvStandingOrder.EndEdit();
    }
}

private void dgvStandingOrder_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{

    dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
    {
        dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
}

2
คำตอบนี้ประกอบด้วยคำตอบที่ถูกต้องซึ่งจัดการทั้งการโต้ตอบของเมาส์และแป้นพิมพ์และการโต้ตอบซ้ำ ๆ โดยไม่ต้องออกจากเซลล์ แต่ต้องใช้ตัวจัดการสุดท้ายเท่านั้นการโทรCommitEditจากCurrentCellDirtyStateChangedเป็นวิธีแก้ปัญหาทั้งหมด
Ben Voigt

2

ทุกอย่างเกี่ยวกับการแก้ไขเซลล์ปัญหาคือเซลล์ไม่ได้แก้ไขจริงดังนั้นคุณต้องบันทึกการเปลี่ยนแปลงของเซลล์หรือแถวเพื่อรับเหตุการณ์เมื่อคุณคลิกกล่องกาเครื่องหมายเพื่อให้คุณสามารถใช้ฟังก์ชันนี้:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

ด้วยสิ่งนี้คุณสามารถใช้งานได้แม้จะอยู่ในเหตุการณ์อื่น


2

ฉันพบคำตอบที่ง่ายกว่าสำหรับปัญหานี้ ฉันแค่ใช้ตรรกะย้อนกลับ รหัสอยู่ใน VB แต่ไม่แตกต่างจาก C # มากนัก

 Private Sub DataGridView1_CellContentClick(sender As Object, e As 
 DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick

    Dim _ColumnIndex As Integer = e.ColumnIndex
    Dim _RowIndex As Integer = e.RowIndex

    'Uses reverse logic for current cell because checkbox checked occures 
     'after click
    'If you know current state is False then logic dictates that a click 
     'event will set it true
    'With these 2 check boxes only one can be true while both can be off

    If DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False And 
       DataGridView1.Rows(_RowIndex).Cells("Column3").Value = True Then
        DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False
    End If

    If DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False And 
    DataGridView1.Rows(_RowIndex).Cells("Column2").Value = True Then
        DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False
    End If


End Sub

หนึ่งในสิ่งที่ดีที่สุดเกี่ยวกับเรื่องนี้คือไม่จำเป็นต้องมีหลายเหตุการณ์


1

สิ่งที่ได้ผลสำหรับฉันคือการCurrentCellDirtyStateChangedใช้ร่วมกับdatagridView1.EndEdit()

private void dataGridView1_CurrentCellDirtyStateChanged( object sender, EventArgs e ) {
    if ( dataGridView1.CurrentCell is DataGridViewCheckBoxCell ) {
        DataGridViewCheckBoxCell cb = (DataGridViewCheckBoxCell)dataGridView1.CurrentCell;
        if ( (byte)cb.Value == 1 ) {
            dataGridView1.CurrentRow.Cells["time_loadedCol"].Value = DateTime.Now.ToString();
        }
    }
    dataGridView1.EndEdit();
}

1

โค้ดจะวนซ้ำใน DataGridView และจะตรวจสอบว่า CheckBox Column ถูกตรวจสอบหรือไม่

private void dgv1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex == 0 && e.RowIndex > -1)
    {
        dgv1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        var i = 0;
        foreach (DataGridViewRow row in dgv1.Rows)
        {
            if (Convert.ToBoolean(row.Cells[0].Value))
            {
                i++;
            }
        }

        //Enable Button1 if Checkbox is Checked
        if (i > 0)
        {
            Button1.Enabled = true;
        }
        else
        {
            Button1.Enabled = false;
        }
    }
}

1

ในเหตุการณ์ CellContentClick คุณสามารถใช้กลยุทธ์นี้:

private void myDataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{    
    if (e.ColumnIndex == 2)//set your checkbox column index instead of 2
    {   //When you check
        if (Convert.ToBoolean(myDataGrid.Rows[e.RowIndex].Cells[2].EditedFormattedValue) == true)
        {
            //EXAMPLE OF OTHER CODE
            myDataGrid.Rows[e.RowIndex].Cells[5].Value = DateTime.Now.ToShortDateString();

            //SET BY CODE THE CHECK BOX
            myDataGrid.Rows[e.RowIndex].Cells[2].Value = 1;
        }
        else //When you decheck
        {
            myDataGrid.Rows[e.RowIndex].Cells[5].Value = String.Empty;

            //SET BY CODE THE CHECK BOX
            myDataGrid.Rows[e.RowIndex].Cells[2].Value = 0;
        }
    }
}

1

ฉันได้ลองหาคำตอบจากที่นี่แล้ว แต่ฉันมักจะมีปัญหาบางอย่างอยู่เสมอ (เช่นดับเบิลคลิกหรือใช้แป้นพิมพ์) ดังนั้นฉันจึงรวมบางส่วนเข้าด้วยกันและมีพฤติกรรมที่สอดคล้องกัน (มันไม่สมบูรณ์แบบ แต่ทำงานได้อย่างถูกต้อง)

void gridView_CellContentClick(object sender, DataGridViewCellEventArgs e) {
  if(gridView.CurrentCell.GetType() != typeof(DataGridViewCheckBoxCell))
    return;
  if(!gridView.CurrentCell.IsInEditMode)
    return;
  if(!gridView.IsCurrentCellDirty)
    return;
  gridView.EndEdit();
}

void gridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e) {
  if(e.ColumnIndex == gridView.Columns["cFlag"].Index && e.RowIndex >= 0)
    gridView.EndEdit();
}

void gridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
  if(e.ColumnIndex != gridView.Columns["cFlag"].Index || e.RowIndex < 0)
    return;

  // Do your stuff here.

}

0

การทำเช่นนี้เมื่อใช้ XtraGrid DevExpress มันเป็นสิ่งที่จำเป็นในการจัดการEditValueChangedกรณีที่มีรายการพื้นที่เก็บข้อมูลที่สอดคล้องกันตามที่อธิบายไว้ที่นี่ สิ่งสำคัญคือต้องเรียกเมธอด gridView1.PostEditor () เพื่อให้แน่ใจว่ามีการโพสต์ค่าที่เปลี่ยนแปลงแล้ว นี่คือการนำไปใช้:

        private void RepositoryItemCheckEdit1_EditValueChanged(object sender, System.EventArgs e)
        {
            gridView3.PostEditor();

            var isNoneOfTheAboveChecked = false;

            for (int i = 0; i < gridView3.DataRowCount; i++)
            {
                if ((bool) (gridView3.GetRowCellValue(i, "NoneOfTheAbove")) && (bool) (gridView3.GetRowCellValue(i, "Answer")))
                {
                    isNoneOfTheAboveChecked = true;
                    break;
                }
            }

            if (isNoneOfTheAboveChecked)
            {
                for (int i = 0; i < gridView3.DataRowCount; i++)
                {
                    if (!((bool)(gridView3.GetRowCellValue(i, "NoneOfTheAbove"))))
                    {
                        gridView3.SetRowCellValue(i, "Answer", false);
                    }
                }
            }
        }

โปรดสังเกตว่าเนื่องจาก xtragrid ไม่ได้มีตัวนับจึงจำเป็นต้องใช้ for loop เพื่อวนซ้ำในแถว


0

การลบโฟกัสหลังจากการเปลี่ยนแปลงค่าของเซลล์ทำให้ค่าสามารถอัปเดตใน DataGridView ลบโฟกัสโดยตั้งค่า CurrentCell เป็น null

private void DataGridView1OnCellValueChanged(object sender, DataGridViewCellEventArgs dataGridViewCellEventArgs)
{
    // Remove focus
    dataGridView1.CurrentCell = null;
    // Put in updates
    Update();
}

private void DataGridView1OnCurrentCellDirtyStateChanged(object sender, EventArgs eventArgs)
{
    if (dataGridView1.IsCurrentCellDirty)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

}

0

คุณสามารถบังคับให้เซลล์ยอมรับค่าได้ทันทีที่คุณคลิกช่องทำเครื่องหมายจากนั้นจับเหตุการณ์CellValueChanged CurrentCellDirtyStateChangedไฟทันทีที่คุณคลิกช่องทำเครื่องหมาย

รหัสต่อไปนี้ใช้ได้กับฉัน:

private void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        SendKeys.Send("{tab}");
    }

จากนั้นคุณสามารถแทรกรหัสของคุณในเหตุการณ์CellValueChanged


0

Ben Voigt พบทางออกที่ดีที่สุดในการตอบกลับความคิดเห็นด้านบน:

private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
        dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

อย่างจริงจังนั่นคือทั้งหมดที่คุณต้องการ


0

ฉันใช้ DataGridView กับ VirtualMode = true และมีเพียงตัวเลือกนี้เท่านั้นที่ใช้ได้สำหรับฉัน (เมื่อทั้งเมาส์และสเปซบาร์ทำงานรวมถึงการคลิกเว้นวรรคซ้ำ ๆ ):

private void doublesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
   var data_grid = (DataGridView)sender;
      
   if (data_grid.CurrentCell.IsInEditMode && data_grid.IsCurrentCellDirty) {
      data_grid.EndEdit();            
   }
}

private void doublesGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
   if (e.ColumnIndex == CHECKED_COLUMN_NUM && e.RowIndex >= 0 && e.RowIndex < view_objects.Count) { // view_objects - pseudocode   
     view_objects[e.RowIndex].marked = !view_objects[e.RowIndex].marked;        // Invert the state of the displayed object
   }
}  
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.