Delphi表格控件:如何即时获取选定行数

4

使用SelectedRows.Count可以获取选中网格行的数量。例如,如果用户选择了3行,则可以在窗体上放置一个按钮,点击该按钮可以显示选定的行数。很好。

但是,如何在用户选择它们时即时更新行数呢?我尝试了许多Grid事件,例如OnColEnterOnMouseDown。它们似乎只在用户单击数据列之外的区域时更新计数,而不是在首次选择行时更新。

由于没有看到与更改Grid组件中的行相关的事件,因此我尝试了底层数据查询中的许多事件,但它们也不一致或常常需要在特定位置单击。我找到的最佳结果(实际代码)是在滚动查询后:

procedure TDataHerd10.QuCowsAfterScroll(DataSet: TDataSet);
begin
  if MenuOpt = 'UpdtInd' then MainView.NumSelEdit.Text:=
    IntToStr(MainView.CowSelGrid.SelectedRows.Count);
end;

这个事件似乎落后了一步,在用户从多行选择回到单行时,初始计数会增加一个。

看起来,如果有正确的事件,我应该能够统计选择的行,并在用户选择/取消选择行时向用户报告?


你尝试过使用 OnClick() 事件吗?这通常是大多数控件的正常事件。在某些控件中,它也会在实际上不是点击的情况下触发,例如使用上/下键滚动组合框。另外,我认为你应该将问题的标题更改为 Data GridTDBGrid,因为我在阅读你的问题时大部分时间都以为你在谈论 TStringGrid,然后不得不重新阅读并理解为 TDBGrid - Jerry Dodge
1
此外,你最好使用复选框。我记不得上一次使用需要按住控制键或Shift键进行多选的应用程序了。除了我曾经获得的古老代码,我实际上已经重写为使用复选框。 - Jerry Dodge
3
@Jerry:嗯...Windows资源管理器?Outlook?Excel?你都不记得使用过它们吗?(这些都是需要使用Ctrl或Shift进行多选的应用程序)?有数百万使用Windows 7的企业用户会不同意你所写的内容。 - Ken White
@Ken 好的,实际上对我来说,资源管理器是如此自然而然,以至于我忘了提到它。至于Outlook,我很少使用,因为没有必要这样做。而Excel则完全不同,那不是一个简单的列表。无论如何,资源管理器和Outlook都有使用复选框的选项,有时当你在触摸屏设备上安装Windows时,默认情况下启用复选框。 - Jerry Dodge
2
@Jerry:我记不得上一个应用程序了。刚刚检查了一下,资源管理器、Outlook和Excel都是应用程序,而且并不是所有的Windows安装都在触摸屏设备上。地球上的大多数人都没有使用触摸屏设备来运行Windows(这包括我网络上的12K多个设备中的大部分)。我的整个办公室刚刚升级到Office 2016,我的Outlook电子邮件文件夹里没有一个复选框(我的收件箱绝对是一个列表)。避免你无法支持的概括性陈述。 :-) - Ken White
2个回答

3

更新:我发现修改原始答案以可靠地满足你的要求在表单首次显示时显示选择计数比我预期的要棘手。

以下是测试项目的基本内容,我希望它可以可靠地按照您的要求运行。除了DBGrid之外,该表单还有一个TEdit,我使用它来确保不会最初聚焦于dbgrid(以便更容易观察dbgrid的行为),以及三个TButton,其功能应从其OnClick处理程序中自明。

您将注意到,捕获dbgrid选择计数的更改的代码仅在dbgrid的OnDrawColumnCell事件中触发。但是,在每次触发时频繁调用此事件(在我的情况下,在表单首次显示之前超过700次)会在GUI中执行其他操作。因此,该表单具有一个变量,用于跟踪选择计数,并且仅在计数更改时更新其显示(在SetSelectedCount setter中)。

type
  TForm1 = class(TForm)
    [...]
  private
    FSelectedCount: Integer;
    procedure SetSelectedCount(const Value: Integer);
  public
    procedure ShowSelectedCount;
    property SelectedCount : Integer read FSelectedCount write SetSelectedCount;
  end;
 [...]

procedure TForm1.btnClearSelectedClick(Sender: TObject);
begin
  DBGrid1.SelectedRows.Clear;
end;

procedure TForm1.btnGetSelectedClick(Sender: TObject);
begin
  ShowSelectedCount;
end;

procedure TForm1.btnSetSelectedClick(Sender: TObject);
begin
  DBGrid1.SelectedRows.CurrentRowSelected := True;
end;

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
    DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
  SelectedCount := DBGrid1.SelectedRows.Count;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ActiveControl := Edit1;  // so the grid does not have focus when the form is first shown
  SelectedCount := -1;
end;

procedure TForm1.SetSelectedCount(const Value: Integer);
begin
  if FSelectedCount <> Value then begin
    FSelectedCount := Value;
    ShowSelectedCount;
  end;
end;

procedure TForm1.ShowSelectedCount;
begin
  Caption := IntToStr(DBGrid1.SelectedRows.Count);
end;

以下是原始回答:

我通常使用 DataSet.AfterScroll 来执行一些非 GUI 的操作,这些操作需要与当前行同步。不幸的是,它不能很好地与 DBGrid 一起使用,正如你已经发现的那样,主要是因为在不滚动数据集的情况下,网格中当前行的选择状态可能会被更改(例如,通过单击它)。

不幸的是,

procedure TForm1.DBGrid1CellClick(Column: TColumn);
begin
  Caption := IntToStr(DBGrid1.SelectedRows.Count);
end;

这种方法并不能完全胜任任务,因为很明显,你可以通过使用键盘来扩展当前行的选择,比如使用Shift + Down

然而,如果你只是加上

procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift:
    TShiftState);
begin
  Caption := IntToStr(DBGrid1.SelectedRows.Count);
end;

这个功能利用了键盘来改变选择项,并且到目前为止,它一直抵御了我的攻击。如果你允许用户在网格中进行就地编辑,你可能需要过滤用于更新选择计数的 Key 值。

顺便说一下,考虑到键盘问题以及 AfterScroll 的问题,我认为你的问题并不应该受到负评,所以我给了它一个 +1。


完美,使用DBGrid1KeyUp!我还可以补充一下@Jerry Doidge的评论,自己创建一个复选框列是个好主意,这样可以使它更加用户友好,并且在我到达非Windows平台时更符合“风格”。 - Scott Stalheim
实际上,@ScottStalheim,我正准备发布我的答案更新,展示一种替代方法,我更喜欢这种方法,因为它只需要一个调用点。 - MartynA
非常感谢您提供的详细帮助,@Martyn。现在我已经成功地让它工作了。您为“ShowSelectedCount”放置按钮的做法是我之前为测试目的所采用的,但需要明确的是,“即时”发布所选数字的要求意味着无需单击按钮即可查看更新的计数。 - Scott Stalheim

1

在@Martyn的优秀回答中,我想再补充一点...

为了实现自动更新显示的计数("即时"),我发现使用Grid1.KeyUp来更新计数非常好,但我还添加了在其他几个事件上的计数更新。最关键的是Grid1.MouseUp。如果没有它,用户可能会单击一个新行,超出当前选择的行,并失去所有行选择,但显示的计数将保持而不是返回零。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接