在Delphi TStringGrid中检测单选还是多选

5
这是我之前关于Delphi字符串网格的问题 Delphi TStringGrid multi select,determining selected rows后续。 这是一个不同的问题。
我更仔细地查看了ONSelectCell事件 TSelectCellEvent = procedure (Sender: TObject; ACol, ARow: Longint; var CanSelect: Boolean) of object;
我注意到TStringGrid.Selection.Top,Bottom属性在事件中不一定准确。 基本上,如果有人从选择多行变为选择一行,那么selection.*属性就不会更新,而如果选择多行,则会更新。
ARow参数无论选择了一个或多个行都会更新,但只有当我确定选择了一行时,它才对我有用。
例如,如果只选择了一行,则使用Arow参数,如果选择了多行,则使用Selection.*属性确定当前选择了哪些行。
一定有更简单的方法....
谢谢!

我不知道你在问什么。"有没有更简单的方法"是一个问题吗? - Warren P
1
在onSelectCell事件中,一定有更简单的方法来知道哪些行被选中了。 - sse
1
@sse - 你是怎么解决这个问题的? - Gabriel
4个回答

6
我认为问题部分在于使用的术语。在完全理解发生了什么之前,可能很难找到“select”是如何同时表示“highlight”和“focus”的。在这种情况下,应该区分这两个概念。
在我继续之前,我想让你记住,聚焦的单元格也可以(实际上也是)高亮显示,但高亮显示的单元格不一定是聚焦的单元格。
现在,“OnSelectCell”事件与聚焦有关。当单元格被点击或者您试图用导航键在其上导航时,处理程序将被触发。简而言之,当尝试聚焦单元格时,处理程序将被调用。您可以通过重置“CanSelect”参数来禁止聚焦单元格(这意味着本质上可以聚焦单元格,因为单元格可以被选择即高亮显示,而不必聚焦,您无法通过“OnSelectCell”控制此行为)。
另一方面,“goRangeSelect”选项和“TDrawGrid.Selection”属性与选择即高亮显示有关。前者允许您(用户)选择多个单元格进行高亮显示,而后者指向这些单元格高亮显示的范围。
现在是我的主要观点。在调用相关处理程序时,“Selection”永远不准确,即它与传递给处理程序的“ACol”和“ARow”参数没有关联。“Selection”包含在调用处理程序之前高亮显示的单元格范围,并且在处理程序内部它永远不会自动更改。无论是一个单元格还是多个单元格,“Selection”在处理程序退出之前保持不变。当发生这种情况(处理程序退出)时,“Selection”才会更改(顺便说一下,结果取决于您是否重置了“CanSelect”)。
因此,总的来说,您不能使用OnSelectCell来确定用户最近一次操作的实际选择结果。相反,我建议按照@Sam的建议使用OnMouseUp*事件。它还允许您控制选择:如果您认为用户选择了“太多”,则可以更正最终范围。在后一种情况下,我可能会考虑改用OnMouseMove,因为它允许您通过“即时”更正范围来更流畅地响应。
如果您只需要确定选择,则OnDrawCell也是可以的。
*根据您的评论,我必须补充说明,您还必须使用OnKeyUp来处理使用键盘进行的选择。

很棒的帖子!感谢您提供详尽的答案。实际上,有些情况下选择是准确的。当多行被突出显示时,TStringGrid.Selection属性在OnSelectCell中是准确的(从突出显示的角度)。使用onMouseUp不是一个好的解决方案,因为事物可以被选中和/或突出显示而无需使用鼠标。我同意onDrawCell是一个好的方法,前提是使用标志来避免不必要的重新计算。我希望有一个onAfterHighlight事件。您对如何将其添加到后代类中有什么想法吗? - sse
我同意,有时处理程序传递的坐标指向已经被选中的单元格。但这只是因为该单元格已经被先前的用户操作选择了,而不是当前用户正在执行的操作。在我的测试中,Selection总是在OnSelectCell之后更新(从处理程序返回之后)。另一方面,你所说的关于OnMouseUp的问题非常真实,到目前为止我不知道其他解决方案(变通方法)除了定义OnKeyUp事件以外。这使得整个方法变得更加不优雅,这是一件遗憾的事情。 - Andriy M

1

我自己解决了这个问题,最终我使用了OnDrawCell事件和onSelectCell事件 -- 我本以为会很麻烦,但结果并不那么糟糕。

以下是我的解决方案摘要,供其他遇到同样问题的人参考。以下是两个关键事实:

  1. TStringGrid.Selection属性在OnDrawCell事件中始终准确。
  2. 仅当选择了多行时,TStringGrid.Selection属性在OnSelectCell事件中才准确。

公共 previousHighlightCount : 整数; //标志以确保在每次行选择时仅调用一次onDraw中必要的代码。 在onFormCreate中初始化为“1”。
过程 Grid.OnDrawCell(...) 开始 ... SelectionCount := Grid.Bottom - Grid.Top;** 如果 ((SelectionCount = 1) AND (previousHighlightCount 1)) then begin GridUpdateEdits; //更新单行网格的例程* previousHighlightCount := 1; end else previousHighlightCount := PrtEdtGrid.SelectionCount; //多个选定行的例程在onSelectCell事件中,而onSeelctCell适用于多个选择。 .... 结束;

感谢那些回复的人!!


0
for RowIndex := StringGrid1.Selection.Top to StringGrid1.Selection.Bottom do
begin
  DoSomethingWithRow(RowIndex);
end;

是的,我明白你的意思。似乎选择属性在 OnSelectCell 事件之后更新,因此在事件内部,您只有旧值(即选择事件之前的值)。对此的答案是将代码移动到 stringgrid 的 OnMouseUp 事件上方。看起来这样可以正常工作。


1
谢谢,这是一个好主意,不幸的是不能保证通过鼠标选择单元格。我的另一个想法是在onDrawCell事件中跟踪选择*,因为它始终准确,但我也不喜欢这个方法,因为它会影响性能(而且似乎有点笨拙)。您有其他想法吗? - sse
1
PS,如果我可以访问到有用的受保护属性,我不介意下降到另一个组件。接下来我会深入研究TStringGrid源代码。 - sse

0

StringGrid1.Selection.Top到StringGrid1.Selection.Bottom是对我来说完美工作的,因为我正在使用onkeypress事件来选择/取消选择东西。


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