ReoGrid 提供 ICellBody 接口和 CellBody 基类用于创建自定义单元格类型。单元格主体嵌入在单元格中,遵循单元格的尺寸并处理渲染、鼠标和键盘事件。

Cell Body

命名空间

using unvell.ReoGrid.CellTypes;
using unvell.ReoGrid.Events;
using unvell.ReoGrid.Rendering;

快速入门

创建一个继承 CellBody 的类,覆写所需的方法,然后将其附加到单元格:

class MyCellBody : CellBody
{
    public override void OnPaint(CellDrawingContext dc)
    {
        dc.DrawCellBackground();
        // 自定义绘制在这里
        dc.DrawCellText();
    }
}

// 附加到单元格
sheet["C3"] = new MyCellBody();
// 或:
sheet.Cells["C3"].Body = new MyCellBody();

移除单元格主体:

sheet.Cells["C3"].Body = null;

CellBody 基类

CellBody 类为所有 ICellBody 成员提供默认实现。只需覆写您需要的方法。

属性

属性类型说明
CellCell父单元格(在 OnSetup 后可用)

生命周期方法

方法返回值说明
OnSetup(Cell cell)void当主体附加到单元格时调用。在此处存储引用
OnSetData(object data)object设置单元格数据时调用。返回修改后的数据或原始数据
OnStartEdit()bool编辑模式前调用。返回 false 以阻止编辑
OnEndEdit(object data, EndEditReason reason)object编辑结束后调用。返回修改后的数据或原始数据
OnEditTextChanged(string text, bool textAppend)(string, string)编辑期间文本变化时调用。返回 (text, selectedText)(null, null)
OnEditKeyDown(string text, KeyCode e)string编辑期间按键时调用。返回新文本或 null
OnGotFocus()void单元格获得焦点时调用
OnLostFocus()void单元格失去焦点时调用
Clone()ICellBody创建此单元格主体的副本

渲染方法

方法返回值说明
OnPaint(CellDrawingContext dc)void渲染单元格主体。默认绘制背景 + 文本
DrawBackground(CellDrawingContext dc)void绘制默认单元格背景
GetBodyBounds()Rectangle获取单元格内的主体边界(考虑内边距)
GetTextAvailableBounds(style)Rectangle获取文本区域边界
CalcCellContentWidth(cell, width)float计算内容宽度
CalcCellContentHeight(cell, height)float计算内容高度

鼠标方法

所有方法返回 bool — 返回 true 以消费事件,false 使用默认行为。

方法说明
OnMouseDown(CellMouseEventArgs e)鼠标按下
OnMouseUp(CellMouseEventArgs e)鼠标释放
OnMouseMove(CellMouseEventArgs e)鼠标在边界内移动
OnMouseEnter(CellMouseEventArgs e)鼠标进入边界
OnMouseLeave(CellMouseEventArgs e)鼠标离开边界
OnMouseWheel(CellMouseEventArgs e)鼠标滚轮滚动(void)

键盘方法

方法返回值说明
OnKeyDown(KeyCode e)bool按键按下。返回 true 以消费
OnKeyUp(KeyCode e)bool按键释放。返回 true 以消费

行为属性

属性类型默认值说明
DisableWhenCellReadonlybooltrue单元格只读时禁用主体
AutoCaptureMouse()booltrue鼠标按下后捕获鼠标事件

CellDrawingContext

CellDrawingContextOnPaint 提供绘图表面:

成员类型说明
CellCell正在渲染的单元格
GraphicsIGraphics平台无关的图形上下文
WorksheetWorksheet父工作表
DrawModeDrawMode当前渲染模式
DrawCellBackground(bool calcScale)void绘制默认单元格背景
DrawCellText()void绘制默认单元格文本

ContentCellBody 基类

对于带有内容区域的单元格主体(如复选框和单选按钮),继承自 ContentCellBody

方法说明
GetContentSize()返回首选内容大小(默认:17x17)
GetContentBounds()获取内容边界,根据单元格样式对齐(HAlign/VAlign)
OnContentPaint(CellDrawingContext dc)覆写以绘制内容区域

自绘

绘制椭圆

class EllipseCell : CellBody
{
    public override void OnPaint(CellDrawingContext dc)
    {
        var bounds = GetBodyBounds();
        dc.Graphics.DrawEllipse(Pens.Blue,
            new Rectangle(0, 0, bounds.Width, bounds.Height));
    }
}

25

绘制默认背景和文本

调用 DrawCellBackground()DrawCellText() 以包含默认渲染:

public override void OnPaint(CellDrawingContext dc)
{
    dc.DrawCellBackground();
    // 在背景和文本之间自定义绘制
    dc.DrawCellText();
    // 在顶部的额外绘制
}

382

绘制对角线

class DiagonalLineCell : CellBody
{
    public bool FromTopLeft { get; set; } = true;

    public override void OnPaint(CellDrawingContext dc)
    {
        var bounds = GetBodyBounds();
        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));
    }
}

sheet["B2"] = new DiagonalLineCell();
sheet["D2"] = new DiagonalLineCell { FromTopLeft = false };

185

禁用单元格编辑

OnStartEdit 返回 false 以阻止编辑:

class ReadOnlyBody : CellBody
{
    public override bool OnStartEdit() => false;
}

修改编辑结果

OnEndEdit 返回修改后的数据:

class UpperCaseBody : CellBody
{
    public override object OnEndEdit(object data, EndEditReason reason)
    {
        if (data is string s)
            return s.ToUpperInvariant();
        return data;
    }
}

处理鼠标事件

class ClickableProgressCell : CellBody
{
    public override bool OnMouseDown(CellMouseEventArgs e)
    {
        var bounds = GetBodyBounds();
        int value = (int)Math.Round(e.RelativePosition.X * 100f / bounds.Width);
        Cell.Worksheet.SetCellData(e.CellPosition, value);
        return true; // 消费事件
    }

    public override void OnPaint(CellDrawingContext dc)
    {
        dc.DrawCellBackground();
        var bounds = GetBodyBounds();
        int value = Cell?.Data is int v ? v : 0;
        float width = bounds.Width * value / 100f;
        dc.Graphics.FillRectangle(new Rectangle(0, 0, width, bounds.Height),
            SolidColor.LightGreen);
        dc.DrawCellText();
    }
}

26 (1)

处理键盘事件

class KeyHandlerCell : CellBody
{
    public override bool OnKeyDown(KeyCode e)
    {
        if (e == KeyCode.Space)
        {
            // 切换值
            Cell.Data = !(Cell.Data is true);
            return true;
        }
        return false;
    }
}

使用公式绑定数据

使用公式绑定两个单元格的数据,使单元格主体响应另一个单元格:

sheet["C3"] = new ClickableProgressCell();
sheet["C7"] = "=C3";  // C7 镜像 C3 的值

27 (1)

拦截数据变更

覆写 OnSetData 以在存储数据之前进行验证或转换:

class ClampedCell : CellBody
{
    public override object OnSetData(object data)
    {
        if (data is double d)
            return Math.Clamp(d, 0.0, 1.0);
        return data;
    }
}

单元格主体边界

主体边界由单元格大小减去内边距计算得出。GetBodyBounds() 方法返回相对于单元格左上角的 Rectangle

public override void OnPaint(CellDrawingContext dc)
{
    var bounds = GetBodyBounds();
    // bounds.X, bounds.Y = 内边距偏移
    // bounds.Width, bounds.Height = 可用区域
}

要更改可用区域,调整单元格的内边距样式:

sheet.Cells["C3"].Style.Padding = new PaddingValue(5, 5, 5, 5);

类层次结构

内置单元格类型在不同层级扩展 CellBody

ICellBody (接口)
  └─ CellBody (基类)
       ├─ ContentCellBody (带对齐的内容区域)
       │    ├─ CheckBoxCell
       │    └─ RadioButtonCell
       ├─ ButtonCell
       │    └─ ImageButtonCell
       ├─ DropdownCell (抽象 — 面板基础设施)
       │    ├─ DropdownListBaseCell
       │    │    ├─ DropdownListCell
       │    │    └─ ComboListCell
       │    ├─ ColumnDropdownListCell
       │    └─ DatePickerCell
       ├─ ProgressCell
       │    └─ NegativeProgressCell
       ├─ HyperlinkCell
       └─ ImageCell

相关主题

这篇文章对您有帮助吗?