DataGridView中的RichTextBox单元格
如何创建DataGridView中的RichTextBox列的源代码
引言
您可以使用此 DataGridViewRichTextBoxColumn
来显示和编辑 RTF 内容。
背景
一位客户希望在他的报告中支持上标和下标,并且他还希望能够编辑这些报告。我认为 RTF 文件是一个不错的选择。然后,问题来了。我需要一个带有 DataGridView
的设置对话框,用户将在其中输入文本(应该支持上标和下标)。所以,我决定将一个 RichTextBox
放入 DataGridView
中。
我在这里找到了一个想法 here。 没有源代码。 所以,我决定写一下它,并在 CodeProject 中发表我的第一篇文章。
创建 DataGridViewColumn 的三个类
这里有一个日历 DataGridViewColumn
的示例 here。
要制作自定义 DataGridViewColumn
,我们应该编写三个类
- 一个从
IDataGridViewEditingControl
派生的编辑器控件类 - 一个从
DataGridViewCell
或其后代类派生的单元格类 - 一个从
DataGridViewColumn
或其后代类派生的列类
首先,编辑器控件类
这是在 RichTextBox
中支持多行输入的代码
public class DataGridViewRichTextBoxEditingControl :
RichTextBox, IDataGridViewEditingControl
{
// To implement multiline, 'Enter' should be treated as an input key.
protected override bool IsInputKey(Keys keyData)
{
Keys keys = keyData & Keys.KeyCode;
if (keys == Keys.Return)
{
return this.Multiline;
}
return base.IsInputKey(keyData);
}
// Tell DataGridView 'Enter' is an input key to this control.
// And some other keys are also input keys.
public bool EditingControlWantsInputKey
(Keys keyData, bool dataGridViewWantsInputKey)
{
switch ((keyData & Keys.KeyCode))
{
case Keys.Return:
// The code for 'Enter' is copied from
// DataGridViewTextBoxEditingControl,
// Shift + Enter = NewLine
if ((((keyData & (Keys.Alt | Keys.Control | Keys.Shift))
== Keys.Shift) && this.Multiline))
{
return true;
}
break;
case Keys.Left:
case Keys.Right:
case Keys.Up:
case Keys.Down:
return true;
}
return !dataGridViewWantsInputKey;
}
}
这是快捷方式的代码。 RichTextBox
已经支持 Ctrl + '+' 和 Ctrl + Shift + '+' 表示下标和上标。 需要做的是添加对 Ctrl + 'B'、Ctrl + 'I' 和 Ctrl + 'U' 的支持。
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Control)
{
switch (e.KeyCode)
{
// Control + B = Bold
case Keys.B:
if (this.SelectionFont.Bold)
{
this.SelectionFont = new Font(this.Font.FontFamily,
this.Font.Size, ~FontStyle.Bold & this.Font.Style);
}
else
this.SelectionFont = new Font(this.Font.FontFamily,
this.Font.Size, FontStyle.Bold | this.Font.Style);
break;
// Control + U = Underline
case Keys.U:
if (this.SelectionFont.Underline)
{
this.SelectionFont = new Font(this.Font.FontFamily,
this.Font.Size, ~FontStyle.Underline & this.Font.Style);
}
else
this.SelectionFont = new Font(this.Font.FontFamily,
this.Font.Size, FontStyle.Underline | this.Font.Style);
break;
// Control + I = Italic
// Conflicts with the default shortcut
//case Keys.I:
// if (this.SelectionFont.Italic)
// {
// this.SelectionFont = new Font(this.Font.FontFamily,
// this.Font.Size, ~FontStyle.Italic & this.Font.Style);
// }
// else
// this.SelectionFont = new Font(this.Font.FontFamily,
// this.Font.Size, FontStyle.Italic | this.Font.Style);
// break;
default:
break;
}
}
}
第二,单元格类
这是 RichTextBoxColumn
最重要的类,因为在这里我们完成了单元格的绘制工作。
我建议您首先查看 here。 然后,您就会知道如何打印 RichTextBox
。 我只是稍微更改了一下,将其打印到 Image
对象。
由于速度问题,单元格类是从 DataGridViewTextBoxCell
派生的。 我用 DataGridViewImageCell
更改了它。 我需要做一些事情来避免 DataGridViewImageCell
中的错误。
public class DataGridViewRichTextBoxCell : DataGridViewImageCell
{
// The value should be RTF string, so these types should be changed.
public override Type ValueType
{
get
{
return typeof(string);
}
set
{
base.ValueType = value;
}
}
public override Type FormattedValueType
{
get
{
return typeof(string);
}
}
// Since the value type is changed, we need to do something more.
private static void SetRichTextBoxText(RichTextBox ctl, string text)
{
try
{
ctl.Rtf = text;
}
catch (ArgumentException)
{
ctl.Text = text;
}
}
public override void InitializeEditingControl
(int rowIndex, object initialFormattedValue,
DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl
(rowIndex, initialFormattedValue, dataGridViewCellStyle);
RichTextBox ctl = DataGridView.EditingControl as RichTextBox;
if (ctl != null)
{
SetRichTextBoxText(ctl, Convert.ToString(initialFormattedValue));
}
}
protected override object GetFormattedValue
(object value, int rowIndex, ref DataGridViewCellStyle cellStyle,
TypeConverter valueTypeConverter,
TypeConverter formattedValueTypeConverter,
DataGridViewDataErrorContexts context)
{
return value;
}
}
现在,我们将绘制单元格。
RichTextBoxPrinter.Print
函数来自我上面提到的 link。
private static readonly RichTextBox _editingControl = new RichTextBox();
// Images for selected and normal states.
private Image GetRtfImage(int rowIndex, object value, bool selected)
{
Size cellSize = GetSize(rowIndex);
if (cellSize.Width < 1 || cellSize.Height < 1)
return null;
RichTextBox ctl = null;
if (ctl == null)
{
ctl = _editingControl;
ctl.Size = GetSize(rowIndex);
SetRichTextBoxText(ctl, Convert.ToString(value));
}
if (ctl != null)
{
// Print the content of RichTextBox to an image.
Size imgSize = new Size(cellSize.Width - 1, cellSize.Height - 1);
Image rtfImg = null;
if (selected)
{
// Selected cell state
ctl.BackColor = DataGridView.DefaultCellStyle.SelectionBackColor;
ctl.ForeColor = DataGridView.DefaultCellStyle.SelectionForeColor;
// Print image
rtfImg = RichTextBoxPrinter.Print(ctl, imgSize.Width, imgSize.Height);
// Restore RichTextBox
ctl.BackColor = DataGridView.DefaultCellStyle.BackColor;
ctl.ForeColor = DataGridView.DefaultCellStyle.ForeColor;
}
else
{
rtfImg = RichTextBoxPrinter.Print(ctl, imgSize.Width, imgSize.Height);
}
return rtfImg;
}
return null;
}
// Draw the image of the rich text box
protected override void Paint(Graphics graphics,
Rectangle clipBounds, Rectangle cellBounds, int rowIndex,
DataGridViewElementStates cellState, object value,
object formattedValue, string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
base.Paint(graphics, clipBounds, cellBounds, rowIndex,
cellState, null, null, errorText, cellStyle, advancedBorderStyle, paintParts);
Image img = GetRtfImage(rowIndex, value, base.Selected);
if (img != null)
graphics.DrawImage(img, cellBounds.Left, cellBounds.Top);
}
单元格编辑所需的其他内容
// Remember, DataGridViewImageCell doesn't behave like DataGridViewTextBoxCell.
// So we need to handle the mouse events for edit.
#region Handlers of edit events, copied from DataGridViewTextBoxCell
private byte flagsState;
protected override void OnEnter(int rowIndex, bool throughMouseClick)
{
base.OnEnter(rowIndex, throughMouseClick);
if ((base.DataGridView != null) && throughMouseClick)
{
this.flagsState = (byte)(this.flagsState | 1);
}
}
protected override void OnLeave(int rowIndex, bool throughMouseClick)
{
base.OnLeave(rowIndex, throughMouseClick);
if (base.DataGridView != null)
{
this.flagsState = (byte)(this.flagsState & -2);
}
}
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
base.OnMouseClick(e);
if (base.DataGridView != null)
{
Point currentCellAddress = base.DataGridView.CurrentCellAddress;
if (((currentCellAddress.X == e.ColumnIndex) &&
(currentCellAddress.Y == e.RowIndex)) &&
(e.Button == MouseButtons.Left))
{
if ((this.flagsState & 1) != 0)
{
this.flagsState = (byte)(this.flagsState & -2);
}
else if (base.DataGridView.EditMode !=
DataGridViewEditMode.EditProgrammatically)
{
base.DataGridView.BeginEdit(false);
}
}
}
}
#endregion
最后,列类
这很容易,因为我们已经有了编辑器控件和单元格的类
public class DataGridViewRichTextBoxColumn : DataGridViewColumn
{
public DataGridViewRichTextBoxColumn()
: base(new DataGridViewRichTextBoxCell())
{
}
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
if (!(value is DataGridViewRichTextBoxCell))
throw new InvalidCastException("CellTemplate must" +
" be a DataGridViewRichTextBoxCell");
base.CellTemplate = value;
}
}
}
Using the Code
像使用 DataGridView
的其他列一样使用它。 请参阅演示源代码。
关注点
起初,单元格类是从 DataGridViewTextBoxCell
派生的。 如果文本很短,那没关系。 但是,当 RTF 内容很长或包含图片时,在进入或离开单元格时会变得很慢。 然后,我将父类更改为 DataGridViewImageCell
,现在速度不再是问题。
历史
- 7/19/2014
- 为 ashsanD 添加一些示例代码,以演示如何将特定文本设置为粗体。 例如,用户可以在 datagridview 中搜索一些文本,并且匹配的文本将被突出显示。
- 4/30/2009
- 更新后的类
DataGridViewRichTextBoxCell
(包括 gzobi666 的更正) - 根据 D_Kondrad 的更正更新了
RichTextBoxPrinter
- 更新后的类
- 12/18/2008
- 添加了带有说明的代码