在C# WinForm下做过项目的朋友都知道其中的DataGridView控件默认只支持DataGridViewButtonColumnDataGridViewCheckBoxColumnDataGridViewComboBoxColumnDataGridViewImageColumnDataGridViewLinkColumn和DataGridViewTextBoxColumn六种列类型如果你想要在DataGridView的列中添加其它的子控件则需要自己实现DataGridViewColumn和DataGridViewCell这就意味着你需要从现有的列中继承并改写一些方法如实现一个支持单选按钮的列或支持三种选择状态的多选按钮的列
上面两个截图分别为RadioButton列和支持三种状态的CheckBox列在DataGridView中的实现效果我是在Windows 中实现的因此显示的效果跟在XP和Vista下有些区别Vista下CheckBox的第三种状态(不确定状态)显示出来的效果是一个实心的蓝色方块
下面我看具体来看看如何实现这两种效果
要实现自定义的DataGridView列你需要继承并改写两个类一个是基于DataGridViewColumn的一个是基于DataGridViewCell的因为RadionButton和CheckBox的实现原理类似因此我们可以将这两种列采用同一种方法实现创建DataGridViewDisableCheckBoxCell和DataGridViewDisableCheckBoxColumn两个类分别继承自DataGridViewCheckBoxCell和DataGridViewCheckBoxColumn代码如下
public class DataGridViewDisableCheckBoxCell: DataGridViewCheckBoxCell
{
public bool Enabled { get; set; }
// Override the Clone method so that the Enabled property is copied
public override object Clone()
{
DataGridViewDisableCheckBoxCell cell = (DataGridViewDisableCheckBoxCell)baseClone();
cellEnabled = thisEnabled;
return cell;
}
// By default enable the CheckBox cell
public DataGridViewDisableCheckBoxCell()
{
thisEnabled = true;
}
// Three state checkbox column cell
protected override void Paint(Graphics graphics Rectangle clipBounds Rectangle cellBounds int rowIndex
DataGridViewElementStates elementState object value object formattedValue string errorText
DataGridViewCellStyle cellStyle DataGridViewAdvancedBorderStyle advancedBorderStyle DataGridViewPaintParts paintParts)
{
// The checkBox cell is disabled so paint the border background and disabled checkBox for the cell
if (!thisEnabled)
{
// Draw the cell background if specified
if ((paintParts & DataGridViewPaintPartsBackground) == DataGridViewPaintPartsBackground)
{
SolidBrush cellBackground = new SolidBrush(cellStyleBackColor);
graphicsFillRectangle(cellBackground cellBounds);
cellBackgroundDispose();
}
// Draw the cell borders if specified
if ((paintParts & DataGridViewPaintPartsBorder) == DataGridViewPaintPartsBorder)
{
PaintBorder(graphics clipBounds cellBounds cellStyle advancedBorderStyle);
}
// Calculate the area in which to draw the checkBox
CheckBoxState state = CheckBoxStateMixedDisabled;
Size size = CheckBoxRendererGetGlyphSize(graphics state);
Point center = new Point(cellBoundsX cellBoundsY);
centerX += (cellBoundsWidth sizeWidth) / ;
centerY += (cellBoundsHeight sizeHeight) / ;
// Draw the disabled checkBox
CheckBoxRendererDrawCheckBox(graphics center state);
}
else
{
// The checkBox cell is enabled so let the base class handle the painting
basePaint(graphics clipBounds cellBounds rowIndex elementState value formattedValue errorText cellStyle advancedBorderStyle paintParts);
}
}
}
public class DataGridViewDisableCheckBoxColumn : DataGridViewCheckBoxColumn
{
public DataGridViewDisableCheckBoxColumn()
{
thisCellTemplate = new DataGridViewDisableCheckBoxCell();
}
}
主要是要实现DataGridViewDisableCheckBoxCell的呈现方式其中设置了CheckBoxState的状态为MixedDisabled表示支持三种状态这个是实现效果的核心如果要实现RadioButton列的效果只需要将Paint方法改成下面这样即可
protected override void Paint(Graphics graphics Rectangle clipBounds Rectangle cellBounds int rowIndex
DataGridViewElementStates elementState object value object formattedValue string errorText
DataGridViewCellStyle cellStyle DataGridViewAdvancedBorderStyle advancedBorderStyle DataGridViewPaintParts paintParts)
{
// Draw the cell background if specified
if ((paintParts & DataGridViewPaintPartsBackground) == DataGridViewPaintPartsBackground)
{
SolidBrush cellBackground = new SolidBrush(cellStyleBackColor);
graphicsFillRectangle(cellBackground cellBounds);
cellBackgroundDispose();
}
// Draw the cell borders if specified
if ((paintParts & DataGridViewPaintPartsBorder) == DataGridViewPaintPartsBorder)
{
PaintBorder(graphics clipBounds cellBounds cellStyle advancedBorderStyle);
}
// Calculate the area in which to draw the checkBox
RadioButtonState state = value != null && (SelectedStatus)value == SelectedStatusSelected ? RadioButtonStateCheckedNormal : RadioButtonStateUncheckedNormal;
Size size = RadioButtonRendererGetGlyphSize(graphics state);
Point center = new Point(cellBoundsX cellBoundsY);
centerX += (cellBoundsWidth sizeWidth) / ;
centerY += (cellBoundsHeight sizeHeight) / ;
// Draw the disabled checkBox
RadioButtonRendererDrawRadioButton(graphics center state);
}
使用RadioButtonState代替CheckBoxState
当然上面的代码只是实现了列和单元格的显示效果在使用过程中当用户点击单选或多选按钮时如何改变状态则需要自己手动编写代码来实现如在点击单选按钮时将DataGridView中其它行的单选按钮设置为未选择状态点击多选按钮时在三种状态之间转换等
首先我们需要手动修改Form的Designercs文件中的代码将CheckBox所在的列的类型由DataGridViewCheckBoxColumn改成DataGridViewDisableCheckBoxColumn并设置ThreeState的值为true这个代码是需要手动去修改的因为默认情况下VS不支持对自定义DataGridView列类型进行可视化编辑要支持CheckBox的三种状态我们还需要定义一个枚举来给CheckBox的TrueValueFalseValue和IndeterminateValue赋值这个枚举很简单有三个成员就够了
public enum SelectedStatus
{
Selected
NoSelected
Indeterminate
}
然后设置CheckBox的TrueValue=SelectedStatusSelectedFalseValue=SelectedStatusNoSelectedIndeterminateValue=SelectedStatusIndeterminate
好了!这个时候运行程序可以看到经过我们改造的列已经可以正常显示了但是有一个问题
那就是当我们点击其中的单选或多选按钮时它的状态并不能发生变化这是因为我们没有在click事件中改变按钮的选择状态要实现这个功能你需要给宿主DataGridView定义CellContentClick事件并且判断当用户点击的是否为你所指定的控件然后进行相应的处理代码如下
private void dataGridView_CellContentClick(object sender DataGridViewCellEventArgs e)
{
if (eRowIndex >= )
{
DataGridViewColumn column = dataGridViewColumns[eColumnIndex];
if (column is DataGridViewCheckBoxColumn)
{
DataGridViewDisableCheckBoxCell cell = dataGridViewRows[eRowIndex]Cells[eColumnIndex] as DataGridViewDisableCheckBoxCell;
if (!cellEnabled)
{
return;
}
if ((SelectedStatus)cellValue == SelectedStatusNoSelected)
{
cellValue = SelectedStatusSelected;
}
else if ((SelectedStatus)cellValue == SelectedStatusSelected)
{
cellValue = SelectedStatusIndeterminate;
}
else
{
cellValue = SelectedStatusNoSelected;
}
}
}
}
这个是CheckBox的如果是RadioButton的话你还需要控制其它RadionButton的状态这个时候就没有三种状态而是两种状态了代码可以修改成这样
private void dataGridView_CellContentClick(object sender DataGridViewCellEventArgs e)
{
if (eRowIndex >= )
{
DataGridViewColumn column = dataGridViewColumns[eColumnIndex];
if (column is DataGridViewCheckBoxColumn)
{
DataGridViewDisableCheckBoxCell cell = dataGridViewRows[eRowIndex]Cells[eColumnIndex] as DataGridViewDisableCheckBoxCell;
if (!cellEnabled)
{
return;
}
if ((SelectedStatus)cellValue == SelectedStatusNoSelected)
{
cellValue = SelectedStatusSelected;
SetRadioButtonValue(cell);
}
else
{
cellValue = SelectedStatusNoSelected;
}
}
}
}
private void SetRadioButtonValue(DataGridViewDisableCheckBoxCell cell)
{
SelectedStatus status = (SelectedStatus)cellValue;
if (status == SelectedStatusSelected)
{
status = SelectedStatusNoSelected;
}
else
{
status = SelectedStatusSelected;
}
for (int i = ; i < dataGridViewRowsCount; i++)
{
DataGridViewDisableCheckBoxCell cel = dataGridViewRows[i]Cells[checkbox] as DataGridViewDisableCheckBoxCell;
if (!celEquals(cell))
{
celValue = status;
}
}
}
函数SetRadionButtonValue负责修改宿主DataGridView当前列中其它的RadionButton的状态
在完成这些工作后一个相对完整的支持RadionButton或三种状态的CheckBox列的DataGridView界面就完成了你可以根据需要在接下来的代码中来判断DataGridView中哪些行被选中了或者哪些行处于未确定的选择状态(CheckBox的第三种状态)进而做出判断来完成后面的工作
最后我会提供整个工程供大家下载其中也给出了DataGridViewLinkColumn列中的一些效果如当鼠标指向超链接时显示一个ToolTip当用户点击超链接时打开一个网页等