2010-05-21 11 views
6

Estoy haciendo un DataGridView con una serie de casillas de verificación con las mismas etiquetas en sentido horizontal y vertical. Cualquier etiqueta que sea igual, las casillas de verificación estarán inactivas, y solo quiero que uno de los dos "controles" para cada combinación sea válido. La siguiente captura de pantalla muestra lo que tengo: DataGridView http://i46.tinypic.com/2e4m3pz.pngDataGridView CheckBox events

Cualquier cosa que esté marcada en la mitad inferior, quiero que no esté marcada en la parte superior. Entonces, si [quux, spam] (o [7, 8] para coordenadas basadas en cero) está marcado, quiero [spam, quux] ([8, 7]) desmarcado. Lo que tengo hasta ahora es la siguiente:

dgvSysGrid.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders; 
    dgvSysGrid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; 
    string[] allsysNames = { "heya", "there", "lots", "of", "names", "foo", "bar", "quux", "spam", "eggs", "bacon" }; 

    // Add a column for each entry, and a row for each entry, and mark the "diagonals" as readonly 
    for (int i = 0; i < allsysNames.Length; i++) 
    { 
     dgvSysGrid.Columns.Add(new DataGridViewCheckBoxColumn(false)); 
     dgvSysGrid.Columns[i].HeaderText = allsysNames[i]; 
     dgvSysGrid.Rows.Add(); 
     dgvSysGrid.Rows[i].HeaderCell.Value = allsysNames[i]; 
     // Mark all of the "diagonals" as unable to change 
     DataGridViewCell curDiagonal = dgvSysGrid[i, i]; 
     curDiagonal.ReadOnly = true; 
     curDiagonal.Style.BackColor = Color.Black; 
     curDiagonal.Style.ForeColor = Color.Black; 
    } 

    // Hook up the event handler so that we can change the "corresponding" checkboxes as needed 
    //dgvSysGrid.CurrentCellDirtyStateChanged += new EventHandler(dgvSysGrid_CurrentCellDirtyStateChanged); 
    dgvSysGrid.CellValueChanged += new DataGridViewCellEventHandler(dgvSysGrid_CellValueChanged); 

} 

void dgvSysGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e) 
{ 
    Point cur = new Point(e.ColumnIndex, e.RowIndex); 

    // Change the diagonal checkbox to the opposite state 
    DataGridViewCheckBoxCell curCell = (DataGridViewCheckBoxCell)dgvSysGrid[cur.X, cur.Y]; 
    DataGridViewCheckBoxCell diagCell = (DataGridViewCheckBoxCell)dgvSysGrid[cur.Y, cur.X]; 
    if ((bool)(curCell.Value) == true) 
    { 
     diagCell.Value = false; 
    } 
    else 
    { 
     diagCell.Value = true; 
    } 
} 

/// <summary> 
/// Change the corresponding checkbox to the opposite state of the current one 
/// </summary> 
/// <param name="sender"></param> 
/// <param name="e"></param> 
void dgvSysGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e) 
{ 
    Point cur = dgvSysGrid.CurrentCellAddress; 

    // Change the diagonal checkbox to the opposite state 
    DataGridViewCheckBoxCell curCell = (DataGridViewCheckBoxCell)dgvSysGrid[cur.X, cur.Y]; 
    DataGridViewCheckBoxCell diagCell = (DataGridViewCheckBoxCell)dgvSysGrid[cur.Y, cur.X]; 
    if ((bool)(curCell.Value) == true) 
    { 
     diagCell.Value = false; 
    } 
    else 
    { 
     diagCell.Value = true; 
    } 
} 

El problema viene es que el valor de la celda ha cambiado siempre parece ser "uno detrás" en el que realmente hace clic si uso el evento CellValueChanged, y no estoy seguro cómo obtener la celda actual si estoy en el estado "sucio" ya que curCell aparece como nulo (sugiriendo que la dirección actual de la célula es incorrecta de alguna manera, pero no intenté sacar ese valor) lo que significa que la ruta no es así t trabajando en absoluto.

Básicamente, ¿cómo obtengo la dirección "correcta" con el valor booleano correcto para que mi algoritmo de volteo funcione?

Respuesta

16

Al final, fue el evento CurrentCellDirtyStateChanged que lo hace, pero hay que hacerlo de la manera correcta. Y el camino correcto es MSDN, aunque no tiene sentido a primera vista.

Un fragmento de lo alto, lo que en última instancia es lo hice a continuación:

// Hook up the event handler so that we can change the "corresponding" checkboxes as needed 
    dgvSysGrid.CurrentCellDirtyStateChanged += new EventHandler(dgvSysGrid_CurrentCellDirtyStateChanged); 
    dgvSysGrid.CellValueChanged += new DataGridViewCellEventHandler(dgvSysGrid_CellValueChanged); 

} 

void dgvSysGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e) 
{ 
    Point cur = new Point(e.ColumnIndex, e.RowIndex); 

    // Change the diagonal checkbox to the opposite state 
    DataGridViewCheckBoxCell curCell = (DataGridViewCheckBoxCell)dgvSysGrid[cur.X, cur.Y]; 
    DataGridViewCheckBoxCell diagCell = (DataGridViewCheckBoxCell)dgvSysGrid[cur.Y, cur.X]; 
    if ((bool)(curCell.Value) == true) 
    { 
     diagCell.Value = false; 
    } 
    else 
    { 
     diagCell.Value = true; 
    } 
} 

void dgvSysGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e) 
{ 
    if (dgvSysGrid.IsCurrentCellDirty) 
    { 
     dgvSysGrid.CommitEdit(DataGridViewDataErrorContexts.Commit); 
    } 
} 

Básicamente, todo lo que está sucediendo es el caso CurrentCellDirtyStateChanged activa el evento CellValueChanged, y eso es todo. Si solo adjunta el evento CellValueChanged, solo se activa DESPUÉS de que haya salido de la celda. No sé exactamente por qué (considerando que es una casilla de verificación, ¿no está "listo" de inmediato?), Pero eso es lo que sucede. Y el código anterior funciona, ya que los cambios de la casilla de verificación van de DERECHA cuando se hace clic. Entonces funciona

+0

¡SÍ! ... mi búsqueda ha terminado ... ahora puedo vivir feliz para siempre. Gracias. – zmaster

2

Puede utilizar el evento CellValidating y el e.FormattedValue tendrá el valor cambiado. Si realiza alguna comprobación y no desea que el valor se actualice, configure e.Cancelar en verdadero.

Aquí está el ejemplo de la página FormattedValue:

private void dataGridView1_CellValidating(object sender, 
    DataGridViewCellValidatingEventArgs e) 
{ 
    dataGridView1.Rows[e.RowIndex].ErrorText = ""; 
    int newInteger; 

    // Don't try to validate the 'new row' until finished 
    // editing since there 
    // is not any point in validating its initial value. 
    if (dataGridView1.Rows[e.RowIndex].IsNewRow) { return; } 
    if (!int.TryParse(e.FormattedValue.ToString(), 
     out newInteger) || newInteger < 0) 
    { 
     e.Cancel = true; 
     dataGridView1.Rows[e.RowIndex].ErrorText = "the value must be a non-negative integer"; 
    } 
} 
+0

Desafortunadamente, esto no funciona, ya que el evento 'CellValidating' solo se activa cuando DEJO la celda, no cuando se hace clic en la casilla y el estado cambia. –

0

La técnica más simple que he encontrado es:

  • registro de grid_CellContentClick
  • examinar ya sea todas las filas de la célula mediante el siguiente, o simplemente la fila desde el controlador de eventos. Mi código requería volver a crear un modelo de datos del estado completo de las celdas cliqueadas.

EditedFormattedValue es el "nuevo" valor de la celda y el valor que se debe leer.

.