11

我想在某个列的每个单元格中放入TCheckBox一个在 Delphi 中的内部。TStringGrid我正在使用德尔福 XE。

4

3 回答 3

29

如果启用,您应该绘制自己的复选框,最好使用视觉主题。这是如何做到这一点的简单草图:

const
  Checked: array[1..4] of boolean = (false, true, false, true);

procedure TForm4.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
const
  PADDING = 4;
var
  h: HTHEME;
  s: TSize;
  r: TRect;
begin
  if (ACol = 2) and (ARow >= 1) then
  begin
    FillRect(StringGrid1.Canvas.Handle, Rect, GetStockObject(WHITE_BRUSH));
    s.cx := GetSystemMetrics(SM_CXMENUCHECK);
    s.cy := GetSystemMetrics(SM_CYMENUCHECK);
    if UseThemes then
    begin
      h := OpenThemeData(StringGrid1.Handle, 'BUTTON');
      if h <> 0 then
        try
          GetThemePartSize(h,
            StringGrid1.Canvas.Handle,
            BP_CHECKBOX,
            CBS_CHECKEDNORMAL,
            nil,
            TS_DRAW,
            s);
          r.Top := Rect.Top + (Rect.Bottom - Rect.Top - s.cy) div 2;
          r.Bottom := r.Top + s.cy;
          r.Left := Rect.Left + PADDING;
          r.Right := r.Left + s.cx;
          DrawThemeBackground(h,
            StringGrid1.Canvas.Handle,
            BP_CHECKBOX,
            IfThen(Checked[ARow], CBS_CHECKEDNORMAL, CBS_UNCHECKEDNORMAL),
            r,
            nil);
        finally
          CloseThemeData(h);
        end;
    end
    else
    begin
      r.Top := Rect.Top + (Rect.Bottom - Rect.Top - s.cy) div 2;
      r.Bottom := r.Top + s.cy;
      r.Left := Rect.Left + PADDING;
      r.Right := r.Left + s.cx;
      DrawFrameControl(StringGrid1.Canvas.Handle,
        r,
        DFC_BUTTON,
        IfThen(Checked[ARow], DFCS_CHECKED, DFCS_BUTTONCHECK));
    end;
    r := Classes.Rect(r.Right + PADDING, Rect.Top, Rect.Right, Rect.Bottom);
    DrawText(StringGrid1.Canvas.Handle,
      StringGrid1.Cells[ACol, ARow],
      length(StringGrid1.Cells[ACol, ARow]),
      r,
      DT_SINGLELINE or DT_VCENTER or DT_LEFT or DT_END_ELLIPSIS);
  end;
end;

当然,在实际场景中,Checked数组不是常数,您可能希望在单元格绘制事件之间保存s度量和h主题句柄。但原理就在这里。

这里缺少的是改变复选框状态的功能。您可能希望在OnClick处理程序中切换状态。如果您真的很认真,您还希望响应鼠标的运动,并在主题可用时在复选框上显示鼠标悬停效果。

由蓝色编辑:要切换复选框状态此答案说明了如何使用Invalidate方法。

于 2011-03-15T02:17:01.383 回答
9

不要试图TCheckBoxTStringGrid. OnDrawCell而是使用带有 Win32 API函数的网格事件,DrawFrameControl()根据需要在每个单元格内绘制 CheckBox 控件的图像。您可以根据需要使用OnClick/OnMouse...带有网格Objects[][]属性的事件来跟踪每个单元格的选中状态。我发现这更容易管理,因为TStringGrid它不是为托管真正的控件而设计的。

于 2011-03-15T01:20:30.790 回答
3

我使用 Roman Mochalov 的名为ExGridView的虚拟网格,它支持复选框。

我自己修改的 GridView 分支,移植为 Unicode 等,命名为 TExGridView,而不是 TGridView,并且带有复选框的演示在 bitbucket 上作为 / wpostma /exgridview。

ExGridView 组件在属性检查器中有一个 Checkbox 属性,该属性必须设置为 true,然后您必须设置 Column 属性,以便 Column 将复选框类型设置为复选框或单选按钮。然后您必须实现 GetCheckState 事件回调。请参阅 bitbucket 项目中包含的演示。

这段代码的原始来源在这里,但它不能在最近的版本上构建。我的 bitbucket 版本已经过测试,可与 Delphi 2007、2009 以及截至 2016 年的所有最新版本(Delphi 10 Seattle)一起使用。

在此处输入图像描述

于 2011-03-15T02:39:13.873 回答