I am using a DataGridView to display locks (by user, assigned by the application) in the associated database.
The user is presented with a list of current locks, showing the relevant information, and the last visible column is a DataGridViewImageColumn
, with the image fed from the application resources.
There is also a hidden DataGridViewCheckBoxColumn
at the end, which is checked when the user double-clicks the icon for a row, and unchecked if the user double-clicks the same icon for that row, i.e. resets their selection.
What I'm trying to do is, based on the value of the checkbox, swap the image and tooltip in the CellContentDoubleClick
event, as below, but I'm not seeing the change to the icon or the tooltip.
'ProjectLocked' is the name of the DataGridViewImageColumn
and 'DeleteLock' is the name of the DataGridViewCheckBoxColumn
.
private void dgvProjectLocks_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
// do nothing for the header row
if (e.RowIndex < 0) return;
// caste the object to the correct type
var dgv = (DataGridView)sender;
// Do nothing if the clicked column is not the ProjectLocked column
if (dgv.Columns[e.ColumnIndex].Name != "ProjectLocked") return;
// Get the clicked row
using (var currentRow = dgv.Rows[e.RowIndex])
{
using (var deleteCell = currentRow.Cells["DeleteLock"])
{
switch (Convert.ToBoolean(deleteCell.Value))
{
case true:
currentRow.DefaultCellStyle.BackColor = dgv.DefaultCellStyle.BackColor;
currentRow.DefaultCellStyle.ForeColor = dgv.DefaultCellStyle.ForeColor;
deleteCell.Value = false;
currentRow.Cells["ProjectLocked"].Value = Resources.locked;
deleteCell.ToolTipText = Resources.AddProjectLockDeleteToolTip;
break;
case false:
currentRow.DefaultCellStyle.BackColor = Color.LightGray;
currentRow.DefaultCellStyle.ForeColor = Color.DarkGray;
deleteCell.Value = true;
currentRow.Cells["ProjectLocked"].Value = Resources.unlocked;
deleteCell.ToolTipText = Resources.RemoveProjectLockDeleteToolTip;
break;
}
}
}
}
Note: Properties.Resources
is a Factory, it returns a new object each time you ask for one. When you assign a new object from the Resource, the object it replaces is left to the GC. In case of Graphics objects, not a good thing. Better cache these objects and use the cached value.
Here I'm using a Dictionary<bool, Image>
as storage facility:
private Dictionary<bool, Image> DataGridViewLockState = new Dictionary<bool, Image>() {
[true] = Resources.locked,
[false] = Resources.unlocked
};
About the code presented here:
using
statement. You need the referenced object alive, here, those are your Rows / CellsOther:
e.RowIndex
and e.ColumIndex
for negative values, since both can be negative, at some point (you may have disabled Column sorting, doesn't matter, it's procedural)null
or DbNull.Value
. Never mind if the data doesn't come out of a database, better safe than sorryWhen you have changed the current state of the affected Cells, call the [DataGridView].EndEdit()
method. This commits the changes immediately.
It also raises the CellValueChanged
event immediately, before the current event has completed. Keep this in mind if you have code in the CellValueChanged
handler.
Better call EndEdit()
last, to avoid some form of re-entrancy
private void dgvTest_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e) {
var dgv = sender as DataGridView;
if (dgv is null || e.RowIndex < 0 || e.ColumnIndex < 0) return;
if (dgv.Columns[e.ColumnIndex].Name != "ProjectLocked") return;
var currentRow = dgv.Rows[e.RowIndex];
var deleteCell = currentRow.Cells["DeleteLock"];
if (deleteCell.Value is null || deleteCell.Value == DBNull.Value) {
throw new InvalidOperationException("DeleteLock value cannot be null");
}
// Negate the current value, we want to change state
bool value = !(bool)deleteCell.Value;
currentRow.Cells["ProjectLocked"].Value = DataGridViewLockState[value];
currentRow.Cells["ProjectLocked"].ToolTipText = value ? Resources.RowLocked : Resources.RowUnlocked;
currentRow.DefaultCellStyle.BackColor = value ? dgv.DefaultCellStyle.BackColor : Color.LightGray;
currentRow.DefaultCellStyle.ForeColor = value ? dgv.DefaultCellStyle.ForeColor : Color.DarkGray;
deleteCell.Value = value;
dgv.EndEdit();
}