Як виявити зміну події DataGridView CheckBox?


90

У мене є програма winforms, і я хочу запустити деякий код, коли прапорець, вбудований в DataGridViewелемент керування, ставиться / знімається. Кожен захід я пробував

  1. Спрацьовує відразу після CheckBoxнатискання клавіші, але до того, як її перевірений стан зміниться, або
  2. Спрацьовує лише раз, коли CheckBoxвтрачає фокус

Здається, я не можу знайти подію, яка спрацьовує відразу після зміни перевіреного стану.


Редагувати:

Я намагаюся досягти того, що коли перевіряється стан a CheckBoxв одному DataGridView, дані в двох інших DataGridViews змінюються. І все-таки всі події, якими я користувався, дані в інших сітках змінюються лише після того, як CheckBoxу першій DataGridViewпрограє фокус.


2
Ви перевірили CurrentCellDirtyStateChangedподію?
Йоградж Гупта,

Досі виконується лише тоді, коли користувач «залишає» комірку.
PJW

1
Ось стаття MSDN про це: msdn.microsoft.com/en-us/library/… схожа, але трохи інша, як відповідь Killercam
Девід Холл

Відповіді:


96

Щоб обробити подію DatGridViews, CheckedChangedспочатку CellContentClickпотрібно запустити файл fire (який не має CheckBoxпоточного стану es)!, А потім зателефонувати CommitEdit. Це, у свою чергу, активує CellValueChangedподію, яку ви можете використовувати для своєї роботи. Це нагляд від Microsoft . Виконайте щось подібне до наступного ...

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

Сподіваюся, це допоможе.

PS Перевірте цю статтю 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 краще, чудове. Однак я не впевнений, у чому вся суєта навколо подвійного клацання a DataGridViewCheckBox. Це не WPF, і подвійне клацання елемента керування не порушує прив'язку даних, це WinForms. Подвійне клацання може не оновити елемент керування візуально, але це нічого не зламає, і в цьому випадку, можливо, наведене нижче рішення є кращим. Дякую.
MoonKnight

Це відмінно працює , якщо додати один і той же код з CellContentClickв CellContentDoubleClickа. CellMouseUpis буде спрацьовувати, навіть якщо клітинку вибрано, але прапорець не натиснуто - що не є бажаною поведінкою.
торпідна здобич

89

Я знайшов рішення @ Killercam для роботи, але був трохи хитрим, якщо користувач двічі клацнув занадто швидко. Не впевнений, чи знайшли це інші. Я знайшов інше рішення тут .

Він використовує сітку даних CellValueChangedта CellMouseUp. Чанхун пояснює це

"Причиною цього є подія OnCellvalueChanged, яка не запускатиметься, поки DataGridView не вважатиме, що ви завершили редагування. Це має сенс для стовпця TextBox, оскільки OnCellvalueChanged не буде [заважати] спрацьовувати для кожного натискання клавіші, але це не [ мати сенс] для CheckBox. "

Ось це в дії з його прикладу:

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
Я зіткнувся з проблемою подвійного клацання, яку зауважив респондент, і ця спрацювала набагато краще, ніж перше рішення при правильній обробці.
Стів Фергюсон

1
Я також зіткнувся з проблемою подвійного клацання, і це рішення виправило її.
Chris C

Натисніть кнопку "тут" і перегляньте статтю. У мене була та ж проблема з подвійним клацанням.
Лука

4
Що робити, якщо ви перемикаєте перемикач за допомогою пробілу?
Halfgaar,

1
Щоб «виправити» проблему з пробілом, я встановив KeyPreviewу формі true, а коли e.KeyCode == Keys.Space, встановив e.Handled = true. Іншими словами, я просто відключив редагування клавіатури.
Halfgaar

9

Рішення jsturteants працювало чудово. Однак я вирішив зробити обробку у випадку 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оскільки останній буде викликаний, коли користувач клацне в будь-якому місці всередині комірки, тоді як перший викликається лише тоді, коли клацне прапорець.
Джеймі Кітсон,

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

Ось деякий код:

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- це ціле рішення.
Бен Войгт,

4

після 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
            }
        }
    }

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

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

Для цього під час використання devexpress xtragrid необхідно обробити подію 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.


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

Бен Фойгт знайшов найкраще рішення у коментарі-відповіді вище:

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

Серйозно, це ВСЕ, що вам потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.