Como detectar a alteração do evento DataGridView CheckBox?

90

Eu tenho um aplicativo winforms e quero acionar algum código quando uma caixa de seleção incorporada em um DataGridViewcontrole é marcada / desmarcada. Cada evento que tentei também

  1. Aciona assim que CheckBoxé clicado, mas antes que seu estado de seleção mude, ou
  2. Aciona apenas quando CheckBoxperde o foco

Não consigo encontrar o evento que dispara imediatamente após as mudanças de estado verificado.


Editar:

O que estou tentando alcançar é que, quando o estado verificado de um CheckBoxem um DataGridViewmuda, os dados em dois outros DataGridViewmudam. Mesmo assim, todos os eventos que usei, os dados nas outras grades só mudam depois que CheckBoxo primeiro DataGridViewperde o foco.

PJW
fonte
2
Você verificou o CurrentCellDirtyStateChangedevento?
Yograj Gupta
Ainda só é executado quando o usuário 'sai' da célula.
PJW de
1
Aqui está o artigo do MSDN sobre isso: msdn.microsoft.com/en-us/library/… semelhante, mas um pouco diferente da resposta do Killercam
David Hall

Respostas:

96

Para manipular o evento DatGridViews CheckedChanged, você deve primeiro fazer o CellContentClickdisparar (que não tem o CheckBoxestado atual es!) E depois chamar CommitEdit. Isso, por sua vez, disparará o CellValueChangedevento que você pode usar para fazer seu trabalho. Este é um descuido da Microsoft . Faça algo como o seguinte ...

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

Eu espero que isso ajude.

PS Verifique este artigo https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx

Cavaleiro da Lua
fonte
4
Esta é uma boa solução, mas não funciona se o usuário clicar várias vezes. Uma alternativa foi postada abaixo stackoverflow.com/questions/11843488/…
56ka
1
Eu também sugiro NÃO usar essa solução para o problema do clique duplo. A função EndEdit () precisa ser chamada ... encontre o link de @ 56ka e clique no link do artigo!
Lucas de
1
Não demorei muito nessa solução e se a solução do @ 56ka for melhor, ótimo. No entanto, não tenho certeza de qual é a confusão sobre clicar duas vezes em a DataGridViewCheckBox. Isso não é WPF e clicar duas vezes no controle não interrompe a vinculação de dados, é WinForms. Clicar duas vezes pode não atualizar o controle visualmente, mas não quebra nada e, neste caso, talvez a solução abaixo seja a melhor. Obrigado.
MoonKnight de
Isso funciona perfeitamente se você adicionar o mesmo código de CellContentClickem CellContentDoubleClicktambém. CellMouseUpIsso será disparado mesmo se a célula estiver selecionada, mas a caixa de seleção não for marcada - o que não é o comportamento desejado.
presa entorpecida,
88

Descobri que a solução do @ Killercam funcionava, mas era um pouco duvidosa se o usuário clicasse duas vezes rápido demais. Não tenho certeza se outro descobriu que o caso também. Eu encontrei outra solução aqui .

Ele usa o datagrid's CellValueChangede CellMouseUp. Changhong explica que

"O motivo para isso é que o evento OnCellvalueChanged não disparará até que DataGridView pense que você concluiu a edição. Isso faz sentido para uma coluna TextBox, já que OnCellvalueChanged não [se incomodaria] em disparar para cada batida de tecla, mas não [ faz sentido] para um CheckBox. "

Aqui está ele em ação a partir de seu exemplo:

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

E o código para dizer à caixa de seleção que a edição foi concluída quando ela foi clicada, em vez de esperar até que o usuário deixasse o campo:

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();
    }
}
justo
fonte
3
Encontrei o problema do clique duplo observado pelo respondente, e este funcionou muito melhor do que a primeira solução para lidar com isso corretamente.
Steve Ferguson,
1
Também encontrei o problema do clique duplo e esta solução o corrigiu.
Chris C
Clique no botão 'aqui' e confira o artigo. Eu tive o mesmo problema com o clique duplo.
Lucas de
4
E se você alternar a alternância com a barra de espaço?
Halfgaar
1
Para 'consertar' o problema da barra de espaço, configurei KeyPreviewcomo true no formulário e quando e.KeyCode == Keys.Space, configurei e.Handled = true. Em outras palavras, acabei de desabilitar a edição do teclado.
Halfgaar
9

A solução de jsturtevants funcionou muito bem. Porém, optei por fazer o processamento no evento EndEdit. Prefiro essa abordagem (em meu aplicativo) porque, ao contrário do evento CellValueChanged, o evento EndEdit não dispara enquanto você preenche a grade.

Aqui está meu código (parte do qual foi roubado de 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();
    }
}
Mark Ainsworth
fonte
3
Boa resposta, mas é preferível usar em CellContentClickvez de CellMouseUpporque o último será chamado quando o usuário clicar em qualquer lugar dentro da célula, enquanto o primeiro só é chamado quando a caixa de seleção é clicada.
Jamie Kitson
6

Isso também controla a ativação do teclado.

    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.....
    }
Chuck Fecteau
fonte
5

Aqui está um código:

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);
    }
}
Nay Lin Aung
fonte
2
Esta resposta contém a resposta correta, que lida com interações de mouse e teclado e interações repetidas sem sair da célula. Mas apenas o último manipulador é necessário - chamar CommitEditde CurrentCellDirtyStateChangedé toda a solução.
Ben Voigt
4

seguindo Killercam'answer, Meu código

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

e:

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
            }
        }
    }
Nghĩa Lê
fonte
2

É tudo sobre a edição da célula, o problema é que a célula não foi editada na verdade, então você precisa salvar as alterações da célula ou da linha para obter o evento quando clicar na caixa de seleção para poder usar esta função:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

com isso você pode usá-lo até mesmo com um evento diferente.

ahmedcool166
fonte
2

Encontrei uma resposta mais simples para esse problema. Eu simplesmente uso a lógica reversa. O código está em VB, mas não é muito diferente do 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

Uma das melhores coisas sobre isso é a não necessidade de vários eventos.

Jimva
fonte
1

O que funcionou para mim foi CurrentCellDirtyStateChangedem combinação comdatagridView1.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();
}
beetroot123
fonte
1

O código fará um loop em DataGridView e verificará se a coluna CheckBox está marcada

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;
        }
    }
}
E Coder
fonte
1

No evento CellContentClick você pode usar esta estratégia:

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;
        }
    }
}
daniele3004
fonte
1

Tentei algumas respostas daqui, mas sempre tive algum tipo de problema (como clicar duas vezes ou usar o teclado). Então, combinei alguns deles e consegui um comportamento consistente (não é perfeito, mas funciona bem).

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.

}
Félix Severo
fonte
0

Para fazer isso ao usar o xtragrid devexpress, é necessário lidar com o evento EditValueChanged de um item de repositório correspondente, conforme descrito aqui . Também é importante chamar o método gridView1.PostEditor () para garantir que o valor alterado foi postado. Aqui está uma implementação:

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

Observe que, como o xtragrid não fornece um enumerador, é necessário usar um loop for para iterar nas linhas.

Majjam
fonte
0

Remover o foco após as alterações do valor da célula permite que os valores sejam atualizados no DataGridView. Remova o foco definindo CurrentCell como 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);
    }

}
Branden Huggins
fonte
0

Você pode forçar a célula a confirmar o valor assim que clicar na caixa de seleção e capturar o evento CellValueChanged . O CurrentCellDirtyStateChanged dispara assim que você clica na caixa de seleção.

O código a seguir funciona para mim:

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

Você pode então inserir seu código no evento CellValueChanged .

David Ruiz
fonte
0

Ben Voigt encontrou a melhor solução em um comentário-resposta acima:

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

Sério, isso é tudo que você precisa.

Roger M. Wilcox
fonte