【发布时间】:2011-07-15 10:19:57
【问题描述】:
我想在 Delphi 的某个列的每个单元格中的 TStringGrid 中放置一个 TCheckBox。我正在使用 Delphi XE。
【问题讨论】:
标签: delphi delphi-xe tstringgrid tcheckbox
我想在 Delphi 的某个列的每个单元格中的 TStringGrid 中放置一个 TCheckBox。我正在使用 Delphi XE。
【问题讨论】:
标签: delphi delphi-xe tstringgrid tcheckbox
不要尝试将实际的TCheckBox 控件放置在TStringGrid 中。将网格的 OnDrawCell 事件与 Win32 API DrawFrameControl() 函数一起使用,根据需要在每个单元格内绘制 CheckBox 控件的图像。您可以将OnClick/OnMouse... 事件与网格的Objects[][] 属性一起使用,以根据需要跟踪每个单元格的检查状态。我发现这更容易管理,因为 TStringGrid 不是为托管实际控件而设计的。
【讨论】:
如果启用,您应该绘制自己的复选框,最好使用视觉主题。这是如何做到这一点的简单草图:
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 处理程序中切换状态。如果您真的很认真,您还希望响应鼠标的运动,并在主题可用时在复选框上显示鼠标悬停效果。
由蓝色编辑:切换复选框状态,this answer 解释了如何使用Invalidate 方法。
【讨论】:
我使用 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)一起使用。
【讨论】: