DataGridViewHTMLCell - 在 DataGridView 中显示 HTML 标记






4.92/5 (7投票s)
在 DataGridView 中显示 HTML 标记
引言
最近,我希望在 DataGridView 单元格中显示一些 HTML 内容。然而,在网上搜索之后,我没有找到任何符合我需求的内容(我什么都没找到)。于是我决定研究一下编写自定义 DataGridView 单元格需要做些什么。
基本上,要创建自己的自定义单元格,你需要从三个与 DataGridView 控件相关的基类创建/派生。它们是:
- 从 DataGridViewColumn 派生一个类
- 从 DataGridViewCell 派生一个类
- 实现 IDataGridViewEditingControl 接口的类派生
下面是演示应用程序的截图。
此项目的源代码/示例应用程序可以在 GitHub 上找到:https://github.com/OceanAirdrop/DataGridViewHTMLCell
HTML 渲染器
为了在单元格中渲染 HTML 内容,我将使用 HTMLRenderer 库。这是一个非常棒的框架。它轻巧,拥有自己的渲染引擎(没有外部依赖),并且只包含 2 个 .DLL 文件。请在此处查看:https://htmlrenderer.codeplex.com/ 以获取更多信息。
要将 HTMLRenderer 添加到您的项目中,您可以选择使用 nuget 自动添加引用,或者从 codeplex/github 下载 .dll 文件并在 C# 项目中手动引用它们。
从 DataGridViewColumn 派生 (第 1 步,共 3 步)
让我们先看看需要派生的 3 个类中的第一个:DataGridViewColumn 类。MSDN 指出 DataGridViewColumn
类“表示 DataGridView 控件中的逻辑列”。这个类是必需的,并为 DataGridView 的工作提供了框架。它让 DataGridView 知道该列中包含什么类型的单元格。
这是完整的类
代码片段 1:设置 DataGridViewHTMLColumn
public class DataGridViewHTMLColumn : DataGridViewColumn { public DataGridViewHTMLColumn() : base(new DataGridViewHTMLCell()) { } public override DataGridViewCell CellTemplate { get { return base.CellTemplate; } set { if (!(value is DataGridViewHTMLCell)) throw new InvalidCastException("CellTemplate must be a DataGridViewHTMLCell"); base.CellTemplate = value; } } }
简洁明了,嗯?这里没什么可看的。- 基本上,我们创建了一个名为 DataGridViewHTMLColumn
的类。请注意,在构造函数中,我们实例化了一个 DataGridViewHTMLCell
(标有红色)。我们还重写了 CellTemplate
属性。CellTemplate
属性“设置用于创建新单元格的模板”。
从 DataGridViewCell 派生 (第 2 步,共 3 步)
这里将是我们大部分自定义代码所在的地方。DataGridViewCell
类表示 DataGridView 控件中的单个单元格。我们的大部分代码将驻留在此类中。
让我们看一下 Paint 方法
代码片段 2:DataGridViewCell Paint 方法
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 = GenerateHTMLImage(rowIndex, value, base.Selected); if (img != null) { graphics.DrawImage(img, cellBounds.Left, cellBounds.Top); } }
Paint 函数的目的是在屏幕上绘制 HTML。graphics.DrawImage(img, cellBounds.Left, cellBounds.Top);
这行代码实际上执行了绘制,但大部分工作被外包给了另一个函数:GenerateHTMLImage()
。此函数生成将绘制到屏幕上单元格位置的 HTML 图像。
让我们仔细看看 GenerateHTMLImage()
函数
代码片段 3:GenerateHTMLImage 函数
private Image GenerateHTMLImage(int rowIndex, object value, bool selected) { // Step 1: Get Size of DataGridView Cell. Size cellSize = GetSize(rowIndex); if (cellSize.Width < 1 || cellSize.Height < 1) return null; // Step 2: Set HTML Text to render. m_editingControl.Size = GetSize(rowIndex); SetHTMLPanelText(m_editingControl, Convert.ToString(value)); if (m_editingControl != null) { // Step 3: Render HTML Image. Image htmlImage = null; Size fullImageSize; if ( RenderHTMLImage( Convert.ToString(value), cellSize, out htmlImage, out fullImageSize ) == false ) { Console.WriteLine("Failed to Generate HTML image"); return null; } // Step 4: If necessary, add an elipsis (...) to image. if ( fullImageSize.Height > cellSize.Height ) { // there is more html than being displayed!! Lets add some elipsis (...) to the image // to let the user know there is more to display htmlImage = AddElipsisToImage(htmlImage); } return htmlImage; } return null; }
跟随上面代码片段中的红色注释,我们首先要做的是获取单元格的大小。如果宽度或高度小于 1,我们直接返回 null,因为没有什么值得绘制的。
在第 2 步中,我们设置 HTML 的 ".Text" 属性(以便它知道要渲染哪个 HTML)。“value”变量是实际的 HTML 标记。
在第 3 步中,我们调用 RenderHTMLImage()
函数。该函数返回要显示的 HTML 的图像表示。
最后,在第 4 步中,我们检查是否需要在图像的右下角添加省略号。稍后将对此进行详细讨论。
现在让我们看一下 RenderHTMLImage()
函数
代码片段 4:实际渲染 HTML 图像
private bool RenderHTMLImage(string htmlText, Size cellSize, out Image htmlImage, out Size fullImageSize) { bool bResult = true; // Step 1: Set out parameters htmlImage = null; fullImageSize = new System.Drawing.Size(); try { // Step 2: Check for null html text if (string.IsNullOrEmpty(htmlText) == true) htmlText = "This cell has a null value!"; // Need to render image twice! Once to get the full size of image and once to get image clipped to the size of the cell // Step 3: Render the html image using the full height but keep the cell width. htmlImage = HtmlRender.RenderToImage(htmlText, maxWidth: cellSize.Width); // Step 4: Keep a record of the full image size to send back to caller. fullImageSize.Height = htmlImage.Height; fullImageSize.Width = htmlImage.Width; // Step 5: Render the HTML imaage a second time with the cell size width / height htmlImage = HtmlRender.RenderToImage(htmlText, new Size(cellSize.Width, cellSize.Height)); m_editingControl.Text = htmlText; } catch(Exception ex) { bResult = false; } return bResult; }
这里要关注的主要代码部分在步骤 3 和 5 中。本质上,我们是通过第三方 HTMLRenderer 库来渲染 HTML 为图像。
htmlImage = HtmlRender.RenderToImage(htmlText, new Size(cellSize.Width, cellSize.Height));
上面的这行代码是我们利用 HTMLRenderer 库的功能的地方。
实现 IDataGridViewEditingControl 接口 (第 3 步,共 3 步)
起初我没有实现这个接口,但这意味着用户无法与此控件交互。单元格感觉不自然。用户无法将单元格中的文本复制到剪贴板。然后我(最初)尝试使用简单的 TextBox 控件,但这显示了完整的标记文本(我怀疑您不希望用户看到这一点)。让我们看一下代码
代码片段 5:设置 DataGridView 编辑控件
public class DataGridViewHTMLEditingControl : HtmlLabel, IDataGridViewEditingControl { private DataGridView m_dataGridView; private int m_rowIndex; private bool m_valueChanged; public DataGridViewHTMLEditingControl() { this.BorderStyle = BorderStyle.None; AutoSize = false; } }
简而言之,这个类的代码看起来就像上面的代码片段(为了简洁起见,我省略了重写的接口方法)。需要注意的重要一点是,这个类是 HtmlLabel。我们从 HtmlLabel 类派生。因此,DataGridViewHTMLEditingControl
是一个 HtmlLabel。为了满足成为可编辑 DataGridView 控件的要求,我们还需要实现 IDataGridViewEditingControl
接口中的方法。这些接口方法没有什么令人兴奋的地方,因此我没有在此处显示它们。您可以在上面引用的 GitHub 页面上查看它们。
完成了!
至此,我们已完成。就是这样。我们已经实现了创建 DataGridView 控件中自定义单元格所需的全部 3 个类。它可以很好地显示 HTML 内容。但在总结之前,我想先讨论一下上面提到的省略号代码。
附加:在单元格右下角添加省略号 (...)
我在玩弄 HTML 单元格时注意到,如果有更多文本要显示,单元格不会向用户发出任何指示,让他们知道“还有更多”。然后我创建了一个简单的文本单元格,并用随机文本填充它,注意到它显示了一个省略号 (...) 来告诉用户:“嘿!如果你想调整我的大小,还有更多文本供我显示”。这就是省略号的由来。
在上面的代码片段 4 中,您可能注意到我渲染了两次 HTML 图像,并且您可能想:“你为什么要这样做?”。嗯,我第一次渲染图像时,让 HTMLRenderer 渲染完整的文本。这会产生包含完整 HTML 的图像。然后我第二次渲染 HTML,但这次将图像大小限制为单元格的宽度和高度。现在,我们可以检查完整图像的高度是否大于单元格图像的高度。如果是,我们就知道还有更多 HTML 需要显示,可以在单元格中添加省略号。
添加省略号的代码如下:
代码片段 6:向单元格添加省略号
Image AddElipsisToImage(Image img) { // This function will grab a graphics object from the image and then draw another image on-top Graphics g = Graphics.FromImage(img); // elipsis g.DrawImage(Resources.elipsis, new Point(img.Width - Resources.elipsis.Width, img.Height - Resources.elipsis.Height)); return img; }
我发现的唯一限制是,当单元格处于编辑模式时,控件由自身绘制,这意味着(显然)省略号不会显示。但这在意料之中。
结论
这是使用出色的第三方库“HTMLRenderer”创建自定义 HTML DataGridView 单元格所涉及步骤的简要概述。此项目的代码可在 Github 上找到,这意味着您可以自由地根据需要添加、编辑和修改此代码。
资源
HTML 渲染器 |
|
CodeProject.com 上的文章“RichTextBox Cell in a DataGridView”,作者:MrWisdom |
https://codeproject.org.cn/Articles/31823/RichTextBox-Cell-in-a-DataGridView |
如何:通过扩展行为和外观自定义 Windows 窗体 DataGridView 控件中的单元格和列 |
https://msdn.microsoft.com/en-us/library/vstudio/7fb61s43(v=vs.100).aspx |
本文代码 |