4

我将复选框 ( ) 放在第一列TCheckBox的字符串网格 ( ) 中。TStringGrid复选框显示良好,位置正确,鼠标悬停时会发光以响应鼠标。但是,当我单击它们时,它们不会切换。他们对点击做出反应,并突出显示,但最终,实际Checked属性没有改变。更令人费解的是,一旦它们存在,我没有任何代码更改这些值,我什至没有OnClick为这些复选框分配事件。另外,我将这些复选框默认为unchecked,但是当显示时,它们被选中

复选框与添加到列表中的每条记录一起创建,并在记录指针内引用,该记录指针分配给要放置复选框的单元格中的对象。

用于单元格突出显示的字符串网格破解:

type
  THackStringGrid = class(TStringGrid); //used later...

包含复选框的记录:

  PImageLink = ^TImageLink;
  TImageLink = record
    ...other stuff...
    Checkbox: TCheckbox;
    ShowCheckbox: Bool;
  end;

复选框的创建/销毁:

function NewImageLink(const AFilename: String): PImageLink;
begin
  Result:= New(PImageLink);
  ...other stuff...
  Result.Checkbox:= TCheckbox.Create(nil);
  Result.Checkbox.Caption:= '';
end;

procedure DestroyImageLink(AImageLink: PImageLink);
begin
  AImageLink.Checkbox.Free;
  Dispose(AImageLink);
end;

将行添加到网格:

//...after clearing grid...
//L = TStringList of original filenames
if L.Count > 0 then
  lstFiles.RowCount:= L.Count + 1
else
  lstFiles.RowCount:= 2; //in case there are no records
for X := 0 to L.Count - 1 do begin
  S:= L[X];
  Link:= NewImageLink(S); //also creates checkbox
  Link.Checkbox.Parent:= lstFiles;
  Link.Checkbox.Visible:= Link.ShowCheckbox;
  Link.Checkbox.Checked:= False;
  Link.Checkbox.BringToFront;
  lstFiles.Objects[0,X+1]:= Pointer(Link);
  lstFiles.Cells[1, X+1]:= S;
end;

Grid 的 OnDrawCell 事件处理程序:

procedure TfrmMain.lstFilesDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  Link: PImageLink;
  CR: TRect;
begin
  if (ARow > 0) and (ACol = 0) then begin
    Link:= PImageLink(lstFiles.Objects[0,ARow]); //Get record pointer
    CR:= lstFiles.CellRect(0, ARow); //Get cell rect
    Link.Checkbox.Width:= Link.Checkbox.Height;
    Link.Checkbox.Left:= CR.Left + (CR.Width div 2) - (Link.Checkbox.Width div 2);
    Link.Checkbox.Top:= CR.Top;
    if not Link.Checkbox.Visible then begin
      lstFiles.Canvas.Brush.Color:= lstFiles.Color;
      lstFiles.Canvas.Brush.Style:= bsSolid;
      lstFiles.Canvas.Pen.Style:= psClear;
      lstFiles.Canvas.FillRect(CR);
      if lstFiles.Row = ARow then
        THackStringGrid(lstFiles).DrawCellHighlight(CR, State, ACol, ARow);
    end;
  end;
end;

这是单击时的外观...

对鼠标点击有反应但不改变

这可能是什么原因造成的?它绝对不会改变Checked我代码中任何地方的属性。当放置在网格中时,复选框本身会出现一些奇怪的行为。

编辑

我做了一个简短的测试,我TCheckBox在表格上放了一个常规。检查/取消检查很好。然后,在我的表单OnShow事件中,我将复选框更改Parent为这个网格。这一次,我得到了相同的行为,单击时不切换。TCheckBox因此,当 a有另一个控件作为其父控件时,它似乎没有正确反应。如何克服这一点?

4

3 回答 3

9

TStringGridWMCommand处理程序不允许子控件处理消息(InplaceEdit 除外)。

因此,您可以使用例如插入类(基于Peter 的代码)或手动绘制控件,正如某些人所建议的那样。这是插入类的代码:

uses
  Grids;

type
  TStringGrid = class(Grids.TStringGrid)
  private
    procedure WMCommand(var AMessage: TWMCommand); message WM_COMMAND;
  end;

implementation

procedure TStringGrid.WMCommand(var AMessage: TWMCommand);
begin
  if EditorMode and (AMessage.Ctl = InplaceEditor.Handle) then
    inherited
  else
  if AMessage.Ctl <> 0 then
  begin
    AMessage.Result := SendMessage(AMessage.Ctl, CN_COMMAND,
      TMessage(AMessage).WParam, TMessage(AMessage).LParam);
  end;
end;
于 2012-07-30T14:53:20.010 回答
3

在 Delphi7 中,至少我这样做:

fChecked[]您需要在单元格上绘制一个复选框,并使其与指示每行中复选框状态的布尔值数组(此处)保持同步。然后,在DrawCell部分TStringGrid

var
 cbstate: integer;
begin
...
if fChecked[Arow] then cbState:=DFCS_CHECKED else cbState:=DFCS_BUTTONCHECK;
DrawFrameControl(StringGrid.canvas.handle, Rect, DFC_BUTTON, cbState);
...
end;

要让复选框响应空格键,请使用KeyDown事件并强制重绘:

if (Key = VK_SPACE) And (col=ColWithCheckBox) then begin
  fChecked[row]:=not fChecked[row];
  StringGrid.Invalidate;
  key:=0;
end;

该方法需要类似的OnClick方法。

于 2012-07-30T11:59:46.010 回答
0
  1. 您可以在 toReportMode(TListView 仿真)模式下使用 VirtualTreeView 而不是网格吗?

  2. 您可以在诸如 NexusDB 或 TClientDataSet 之类的内存表上使用 TDBGrid 吗?

  3. 丑陋的方法会像带有自定义字体的字母一样呈现复选框 - 例如 WinDings 或http://fortawesome.github.com/Font-Awesome

后者最容易实现,但最难看,最难维护——业务逻辑混入 VCL 事件处理程序

于 2012-07-30T10:39:14.980 回答