2010-05-21 19 views
6

Sto creando uno DataGridView con una serie di checkbox in esso con le stesse etichette in senso orizzontale e verticale. Eventuali etichette uguali, le caselle di controllo saranno inattive e desidero solo che uno dei due "assegni" per ogni combinazione sia valido. La seguente schermata mostra quello che ho: DataGridView http://i46.tinypic.com/2e4m3pz.pngEventi DataGridView CheckBox

Qualsiasi cosa che viene controllata nella metà inferiore, voglio UN-checked sulla parte superiore. Quindi se [quux, spam] (o [7, 8] per le coordinate basate su zero) è selezionato, voglio che [spam, quux] ([8, 7]) non sia spuntato. Quello che ho finora è la seguente:

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

Il problema arriva è che il valore della cella cambiato sempre sembra essere "uno dietro" dove realmente sceglie se uso l'evento CellValueChanged, e io non sono sicuro come ottenere la cella corrente se sono nello stato "sporco" quando curCell viene inserito come null (suggerendo che l'indirizzo corrente della cella sia sbagliato in qualche modo, ma non ho provato a ottenere quel valore) che significa che il percorso non è sto lavorando affatto

In sostanza, come faccio a ottenere l'indirizzo "giusto" con il valore booleano corretto in modo che il mio algoritmo di flipping funzioni?

risposta

16

In ultima analisi, è stato l'evento CurrentCellDirtyStateChanged che lo fa, ma è necessario farlo nel modo giusto. E il modo giusto è MSDN, anche se non ha senso a prima vista.

Un frammento dall'alto, e quello che in ultima analisi, fatto è qui sotto:

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

In sostanza, tutto ciò che sta accadendo è l'evento CurrentCellDirtyStateChanged attiva l'evento CellValueChanged, e il gioco è fatto. Se si allega l'evento CellValueChanged, si attiva solo dopo aver lasciato la cella. Non so perché esattamente (considerando che è una casella di controllo, non è "fatto" immediatamente?), Ma è quello che succede. E il codice come sopra funziona, in quanto la casella di controllo cambia andare a DESTRA quando si fa clic su di esso. Quindi funziona.

+0

SI !! ... la mia ricerca è finita ... Ora posso vivere felice e contento. Grazie. – zmaster

2

È possibile utilizzare l'evento CellValidating e lo e.FormattedValue avrà il valore modificato. Se si esegue un controllo e non si desidera aggiornare il valore, impostare e.Cancel su true.

Ecco l'esempio dalla pagina 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

Questo sfortunatamente non funziona poiché l'evento "CellValidating' si attiva solo quando LASCIA la cella, non quando si fa clic sulla casella di controllo e lo stato cambia. –

0

La tecnica più semplice che ho trovato è:

  • registro per grid_CellContentClick
  • esaminare né tutte le righe della cella utilizzando il sotto, o semplicemente la riga dalla gestore di eventi. Il mio codice richiedeva la ri-creazione di un modello dati dall'intero stato delle celle su cui si è fatto clic.

EditedFormattedValue è il "nuovo" valore della cella e il valore da leggere.

.

var colNumber=0; 
foreach (var row in grid.Rows) { 
    var dataGridViewCell = (DataGridViewCheckBoxCell)((DataGridViewRow)(row)).Cells[colNumber]; 
    if (dataGridViewCell.EditedFormattedValue) { 
     // This cell is now checked - do whatever you need to with the other cells 
    } 
}