I am hoping that someone can shed some light on my issue.
My goal is to take an existing DataGridView
with a DataGridCheckBoxColumn
and add a CheckBox
to its header cell. I found some code while researching my question on StackOverflow and have incorporated it (with some changes) into my existing code.
The class below is the one I derived from the StackOverflow post/example (with some additional modifications):
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AdvancesDistNCPC
{
class DGVCheckBoxHeaderCell : DataGridViewColumnHeaderCell
{
public readonly CheckBox chkBox;
ToolTip chkToolTip = new ToolTip();
private Padding m_ButtonPadding = Padding.Empty;
public DGVCheckBoxHeaderCell(string sCheckText, Color chkForeColor, Color chkBackColor, int chkBoxWidth = 40, string toolTipText = "")
{
chkBox = new CheckBox();// { BackColor = Color.Orange, Width = buttonWidth };
m_ButtonPadding = new Padding(chkBox.Width + 2, 0, 0, 0);
chkBox.Checked = false;
chkBox.Text = sCheckText;
chkBox.TextAlign = ContentAlignment.MiddleCenter;
chkBox.ForeColor = chkForeColor;
chkBox.BackColor = chkBackColor;
if (toolTipText.Length > 0)
{
chkToolTip.SetToolTip(chkBox, toolTipText);
}
chkBox.CheckAlign = ContentAlignment.BottomCenter;
chkBox.CheckedChanged += CheckedChanged;
}
public void ReplaceHeaderCell(DataGridViewColumnHeaderCell previousHeader)
{
if (previousHeader == null) return;
SetStandardValues(previousHeader);
var dgv = previousHeader.DataGridView;
previousHeader.Dispose();
dgv.Columns[previousHeader.OwningColumn.Index].HeaderCell = this;
}
private void SetStandardValues(DataGridViewColumnHeaderCell previous)
{
if (previous == null) return;
this.Style = previous.Style;
chkBox.Font = this.Style.Font ?? previous.DataGridView.ColumnHeadersDefaultCellStyle.Font;
// etc.
}
private void CheckedChanged(object sender, EventArgs e)
{
OnClick(new DataGridViewCellEventArgs(ColumnIndex, RowIndex));
//MessageBox.Show("You clicked me");
}
protected override void OnDataGridViewChanged()
{
if (this.DataGridView == null) return;
this.DataGridView.Controls.Add(chkBox);
}
protected override void Paint(Graphics g, Rectangle clipBounds, Rectangle bounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advBorderStyle, DataGridViewPaintParts parts)
{
cellStyle.Padding = Padding.Add(m_ButtonPadding, cellStyle.Padding);
base.Paint(g, clipBounds, bounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advBorderStyle, parts);
chkBox.Location = new Point(bounds.Left, bounds.Top + 2);
chkBox.Height = bounds.Height - 4;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
chkBox.MouseClick -= CheckedChanged;
chkBox.Dispose();
}
base.Dispose(disposing);
}
}
}
I am instantiating this class after the DataGridView
has been created and populated, and after I run through a loop to format the existing columns.
I am using the following routine to insert the header cell:
private void Add_AdvanceDist_ApproveAll_Checkbox(ref DataGridView dgvParm, string colName, string sToolTipText = "")
{
try
{
int colIndex = GetIndexForColName(dgvParm, colName);
int firstColIdx = dgvParm.FirstDisplayedScrollingColumnIndex;
dgvParm.FirstDisplayedScrollingColumnIndex = colIndex;
var newCheckBoxHeaderCell = new DGVCheckBoxHeaderCell("Approve", Color.White, Color.Green);
newCheckBoxHeaderCell.ReplaceHeaderCell(dgvParm.Columns[colIndex].HeaderCell);
dgvParm.InvalidateCell(colIndex, 0);
dgvParm.Columns[approvedCol].Width = 0;
dgvParm.Refresh();
dgvParm.FirstDisplayedScrollingColumnIndex = firstColIdx;
}
catch (Exception ex)
{
}
}
The code takes the existing DataGridViewCheckBoxColumn
in question, and replaces its HeaderCell
with the new DGVCheckBoxHeaderCell
instance.
So far, the code appears to work (in that the DataGridViewCheckBoxColumn
now contains the DGVCheckBoxHeaderCell
).
However, something weird is going on with the refresh/painting of the DataGridView
. When it is scrolled horizontally, the following happens:
- If the
DataGridViewCheckBoxColumn
is in view, theHeaderCell
stays stationary (i.e., doesn't scroll with the rest of the cells), and - When the
DataGridViewCheckBoxColumn
disappears from view, then theHeaderCell
does finally disappear, but when theDataGridView
is scrolled (back in the other direction) so that theDataGridViewCheckBoxColumn
is in view again, the HeaderCell is not subsequently painted.
It looks like this:
If I scroll back and forth, the effect within the HeaderCell
is like a moving car window, within which the column headers whiz by.
Then, when I click directly on the ``HeaderCell```, it displays this:
which is the result I am expecting to happen automatically.
Obviously, this is a cell painting issue. What I am doing wrong (or not doing at all like I should)?
Any suggestions and/or insights would be greatly appreciated.
Thanks in advance,
Chris Fleetwood [email protected]