将一个TCheckBox放在Delphi中的TStringGrid中

问题描述 投票:10回答:3

我想把一个TCheckBox放在Delphi中的TStringGrid中,在某个列的每个单元格中。我正在使用Delphi XE。

delphi delphi-xe tstringgrid tcheckbox
3个回答
27
投票

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

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处理程序中切换状态。如果您真的很认真,那么您还希望响应鼠标的动作,并在主题可用时在复选框上显示鼠标悬停效果。

EDIT by bluish:要切换复选框状态,this answer解释了如何使用Invalidate方法。


9
投票

不要试图在TCheckBox中放置一个真正的TStringGrid控件。使用网格的OnDrawCell事件代替Win32 API DrawFrameControl()函数,根据需要在每个单元格内绘制CheckBox控件的图像。您可以将OnClick/OnMouse...事件与网格的Objects[][]属性一起使用,以根据需要跟踪每个单元格的已检查状态。我发现这更易于管理,因为TStringGrid并非设计用于托管实际控件。


3
投票

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.