# dotNet.ReoGrid 扩展库 - 自定义单元格

单元格主体
-----------------------

ReoGrid 提供了一个称为 `ICellBody` 的接口，用于创建自定义单元格。单元格主体可以嵌入到单元格中，它会自动适应单元格的尺寸并获取单元格的某些资源访问权限。![单元格主体](../images/24.png)

当单元格主体被分配到单元格后，它会无缝集成到单元格中，自动适应单元格大小并与单元格的属性和方法交互。![单元格主体集成](../images/p061.png)

示例
-----------------

这是一个在单元格内显示红色进度条的单元格主体示例。![23 (1)](../images/23-1.png)

更多示例可在演示项目中查看。

单元格主体接口
-------------------------------------------------

要在 ReoGrid 中创建自定义单元格主体，
我们必须用 C# 代码编写一个继承自 `CellBody` 的类或实现 `ICellBody` 接口。`CellBody` 类提供了多种可重写的方法，用于自定义单元格的行为和外观。

`CellBody` 类作为自定义单元格主体的基类，封装了单元格自定义所需的通用功能和属性。

 [在 GitHub 上查看 CellBody 源代码](https://github.com/unvell/ReoGrid/blob/master/ReoGrid/CellTypes/CellBody.cs)



调用 C#  `CellBody` 或 `ICellBody` 前请确保导入以下命名空间：

```aardio
using System;
using unvell.ReoGrid;
using unvell.ReoGrid.CellTypes;
using unvell.ReoGrid.Events;
using unvell.ReoGrid.Rendering;
using unvell.ReoGrid.Graphics;
using unvell.ReoGrid.Interaction;
```


这个框架通过利用 `CellBody` 类或 `ICellBody` 接口，为扩展 ReoGrid 单元格功能提供了结构化方法。

创建单元格主体
-----------------------------------------

首先我们在 aardio 里调用 C# 编译一个程序集以创建自定义的 MyCellBody 类。
当然也可以在 C# 开发环境内编译。

直接运行下面的 aardio 代码将会创建程序集 ardio.ReoGrid.dll 。

```cs
import dotNet.ReoGrid.Compiler; 
var compiler = dotNet.ReoGrid.Compiler();

//引用程序集
compiler.Reference("System.Drawing");

//C# 源码
compiler.Source = /***

//请确保导入以下命名空间：
using System;
using unvell.ReoGrid;
using unvell.ReoGrid.CellTypes;
using unvell.ReoGrid.Events;
using unvell.ReoGrid.Rendering;
using unvell.ReoGrid.Graphics;
using unvell.ReoGrid.Interaction; 

namespace aardio.ReoGrid{  
	
	//让类继承 CellBody
	class MyCellBody : CellBody {
		
  		public override void OnPaint(CellDrawingContext dc)
  		{
    			// Define the bounds for the ellipse. Here, it fills the entire cell.
    			Rectangle ellipseBounds = new Rectangle(0, 0, base.Bounds.Width, base.Bounds.Height);
			 
    			// Draw an ellipse within the cell using the specified bounds
    			dc.Graphics.DrawEllipse(SolidColor.Blue, ellipseBounds);
  		}
	}
}
***/	
compiler.CompileOrFail("/aardio.ReoGrid.dll");
```

在上面的示例中我们实现了自定义绘图的单元格。

首先我们重写了自定义单元格主体类 `MyCellBody` 中的 `OnPaint` 方法。通过重写 `OnPaint` 方法，可以为单元格执行自定义绘图操作，如绘制形状、文本或图像，从而根据特定需求增强单元格的视觉表现。

在 `OnPaint` 方法中：
* `CellDrawingContext dc`：CellDrawingContext 提供绘图上下文，是用于在单元格边界内绘制形状、文本和其他图形的画布（ Graphics ）对象
* `base.Bounds`：表示单元格的边界，由 ReoGrid 自动维护。这里用它来定义椭圆相对于单元格的大小和位置


将单元格主体设置到单元格
-------------------------------------------------------

要在 ReoGrid 中将自定义单元格主体嵌入到特定单元格，可以使用以下任一方法。这两种方法功能相同，但提供了不同的语法选项：

1. 使用工作表对象的索引器：

```aardio
sheet1["C3"] = MyCellBody();
```

这种方法利用工作表对象的索引器，允许直接将 `MyCellBody`（或任何继承自 `CellBody` 的类）的新实例分配给 C3 位置的单元格。

2. 设置 Cell 对象的 Body 属性：

```aardio
sheet1.Cells["C3"].Body = MyCellBody();
```

这种方法通过工作表的 Cells 属性访问 C3 位置的 `Cell` 对象，然后将单元格的 Body 属性设置为 `MyCellBody` 的新实例。

这两种方法都会用新的 `MyCellBody` 实例替换指定单元格中现有的单元格主体。确保 `MyCellBody` 是一个继承自 ReoGrid 的 CellBody 类的类，这样才能自定义单元格行为和外观。

在 aardio 中创建自定义单元格的完整示例代码如下：

```aardio
import win.ui;
/*DSG{{*/
var winform = win.form(text="ReoGrid - 自定义单元格")
/*}}*/

import dotNet.ReoGrid; 
var grid = ReoGrid.ReoGridControl(winform);
var sheet1 = grid.CurrentWorksheet;
 
//用 $ 操作符内存加载 DLL，发布后生成独立 EXE，不再需要 aardio.ReoGrid.dll
var dll = dotNet.loadFile($"/aardio.ReoGrid.dll");

//导入 C# 名字空间
var gridUtil = dll.import("aardio.ReoGrid");

//加载自定义单元格
sheet1["C3"] = gridUtil.MyCellBody();

winform.show();
win.loopMessage();
```

运行以上 aardio 示例的的显示效果如下：

![25](../images/25-1.png)

从单元格移除单元格主体
---------------------------------------------------------

将单元格实例的 `Body` 属性设为 null 即可移除单元格主体：

```aardio
sheet1.Cells["C3"].Body = null;
```

### 绘制默认单元格文本和背景

要在自定义单元格主体的绘制逻辑中按照 ReoGrid 的默认行为渲染单元格背景和文本，可以调用以下方法：

```aardio
dc.DrawCellBackground();   // 重绘单元格背景
dc.DrawCellText();         // 重绘单元格文本
```

这些方法应该从自定义 `CellBody` 类的 `OnPaint` 方法重写中调用。`dc` 参数指向传递给 `OnPaint` 方法的 `CellDrawingContext` 对象，它提供了单元格渲染的绘图表面和上下文。

在自定义 `CellBody` 类的 C# 代码中，可以这样使用这些方法：

```cs
public override void OnPaint(CellDrawingContext dc)
{
    // 绘制默认单元格背景
    dc.DrawCellBackground();

    // 自定义绘图逻辑

    // 在背景上绘制默认单元格文本
    dc.DrawCellText();

    // 可以继续添加其他自定义绘图逻辑
}
```

示例结果：![382](../images/382.png)

### [示例：在单元格内绘制对角线](#示例在单元格内绘制对角线)

以下是在单元格内绘制对角线的 C# 代码示例：

```cs
class DiagonalLineCell : CellBody
{
  public bool FromTopLeft { get; set; }

  public DiagonalLineCell() { this.FromTopLeft = true; }

  public override void OnPaint(unvell.ReoGrid.RGDrawingContext dc)
  {
   if (FromTopLeft) {
     dc.Graphics.DrawLine(Pens.Black, new Point(0, 0), new Point(Bounds.Right, Bounds.Bottom));
   } else {
     dc.Graphics.DrawLine(Pens.Black, new Point(0, Bounds.Bottom), new Point(Bounds.Right, 0));
   }
  }
}
```

在 aardio 中将上面的两个实例添加到工作表中：

```aardio
var dll = dotNet.loadFile($"/aardio.ReoGrid.dll");
var gridUtil = dll.import("aardio.ReoGrid");

sheet1["B2"] = gridUtil.DiagonalLineCell();
sheet1["D2"] = table.assign( gridUtil.DiagonalLineCell() ,{FromTopLeft=false} );
```

结果：![185](../images/185.png)

禁用单元格编辑
---------------------------------------

在 C# 代码中通过重写 `OnStartEdit` 方法并返回 false 来阻止默认编辑操作。

```cs
private class MyCellBody : CellBody
{
  public override bool OnStartEdit()
  {
    return false;
  }
}
```

修改编辑结果
-----------------------------------------

通过重写 `OnEndEdit` 方法并返回数据来替换用户编辑结果。

处理鼠标和键盘事件
---------------------------------------------------------------------

在 C# 代码中通过重写如 `OnKeyDown` 和 `OnMouseDown` 等方法来处理键盘和鼠标事件。

```cs
public override bool OnMouseDown(CellMouseEventArgs e)
{
  // translate cursor position to percent
  int value = (int)Math.Round(x * 100f / (Bounds.Width));

  // update cell's data, but do not set the data to cell directly, call methods of control instead
  sheet.SetCellData(e.CellPosition, value);
}
```

![26 (1)](../images/26-1.png)

使用公式绑定数据
---------------------------------------------------------

可以使用公式绑定两个单元格的数据：

```aardio
sheet1["C7"] = "=E7";
```

![27 (1)](../images/27-1.png)

内置单元格类型
-------------------------------------------

ReoGrid 提供了许多内置单元格类型，如按钮、复选框、下拉列表、图像等。详见[内置单元格类型](../cell/built-in-cell-types "内置单元格类型".html)。