3

我在使用 TStringGrid 和弹出菜单时遇到问题

我想知道从弹出菜单中选择一个项目时上次活动的单元格的行/列。但是,当我单击弹出菜单时,StringGrid.Row 将返回为 -1。

我尝试使用 MouseToCell 作为 OnClick 的一部分,但即使在设置 SG.Row 之后,它仍然在 PopUp 菜单例程中返回为 -1 ......我怀疑问题是 Grid 失去了焦点。

有没有不需要 OnClick 设置全局变量的解决方案?

我正在使用链接到弹出菜单上的项目的操作列表来确保工具栏和弹出菜单之间的操作是一致的

4

5 回答 5

10

恐怕我不完全明白你的意思。当我左键单击字符串网格中的单元格时,它会被选中,但当我右键单击它时不会。当我右键单击它时,将显示弹出菜单(如果已分配),并且MenuItemClick我可以轻松阅读当前选择的row和。col请参阅示例视频

你实际上想要这个:你想要右键单击来更改活动单元格以及左键单击。这很容易做到:

procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
    StringGrid1.Perform(WM_LBUTTONDOWN, 0, MakeLParam(Word(X), Word(Y)));
end;
于 2010-08-19T16:10:35.973 回答
1

了解 TStringGrid 上选择的行的另一种方法(它实际上是唯一的):

YourstringGrid.Selection.Top;
YourstringGrid.Selection.Bottom;

如果只选择了一行,它们必须匹配。

我从来没有看到.Selection.···失败,虽然我看到 YourstringGrid.Row 似乎无法获取选择行,但很多时候它返回 -1 当您认为它必须返回其他值时(请参阅最后的 4 点以了解为什么似乎失败但当它返回-1时,它真的不是失败,......这是一个理解的概念)。

选择和带有焦点的单元格不是一回事.......Selection用于选择,.Row并且.Col用于具有焦点的单元格并且与选择无关,它可以是具有焦点的单元格,而选择是完全不同的单元格范围(两者是不同的概念)。

此外,我发现这YourstringGrid.Row<>YourstringGrid.Selection.Top可能是真的。当具有焦点的单元格不在所选内容的第一行时。

Internet 上显示的一些 hack、技巧、代码等仅适用于goRowSelect=False如果设置为 True 此类例程无法正常工作的情况,请谨慎使用。

提示:在 TStringGrid 上,goRowSelect=True通过代码选择多行是非常错误的,.Selection有时更改不会更新.Row(它们不会更改具有焦点的实际单元格),所以如果有人只想选择一行,那就是最好将行值直接分配给.Row.

请记住:在 TStringGrid 上goRowSelect=True谈论什么单元格有焦点是没有意义的,所以在编码时他们根本没有想到这样的事情(.Row并且.Col不能在什么时候阅读goRowSelect=True)。换句话说:如果你总是选择一整行,那么必须检查有焦点的单元格,没有这样的单元格,它是整行,等等......认为不要被 BUG 激怒在 TStringGrid 上混合焦点单元格时的内部实现goRowSelect=True

同样最糟糕的是:WhithgoRowSelect=True和一些键盘组合(Shift+Cursors)和鼠标点击,你可以做出罕见的选择,比如一列中有两个或三个单元格,但不是整行;记住它有goRowSelect=True并且网格只显示选择的行的一些单元格,如果你读.Selection它告诉你行上的所有单元格都没有被选中(True=(TheGrid.FixedCols+#<TheGrid.Selection.Left)) 其中# 可以大于 1。同样,请注意此类 BUG...我只能说...我总是捕获选择更改并强制选择完整的行(我在 OnSelectCell 上放置代码以确保所有选择都是完整的row/s),看一个简单的代码(注意我不关心正在选择什么单元格,从概念上讲,选择必须进入整行或整行,而不是单元格;又是一个罕见的概念,内部实现不认为你想要在选定的单元格更改时做一些事情,所以这个事件应该没有代码,我把这个代码用来修复选择不是全行的BUG):

procedure TYourForm.YourGridSelectCell(Sender: TObject; ACol, ARow: Integer;  var CanSelect: Boolean);
begin
     YourGrid.Selection:=TGridRect(Rect(YourGrid.FixedCols,YourGrid.Selection.Top,YourGrid.FixedCols,YourGrid.Selection.Bottom));
end;

简化:TStringGrid 有太多错误,我发现它同时告诉 '.Row=13'.Selection.Top=2.Selection.Bottom=5; 选择之外的活动行怎么可能是第一行?等等。这是因为'.Row'(以及.Col)不谈论选择的行,谈论什么单元格有焦点,所以看到.Row知道选择了什么行在概念上是错误的......你会看到行具有焦点的单元格,与实际选择单元格无关。

我从来没有见过失败的事情:

  • .Selection.···总是告诉你选中的区域(区域显示为选中)
  • .Row 如果分配了一个值(并且网格有goRowSelect=True),则选择整行(我从来没有尝试过没有goRowSelect=True),但只有一行。

更不用说您是否想大量破解 TStringGrid 并使其成为同时具有多个连续选择的多行选择(例如 ListBox 多选);由于 TStringGrid 在 .Row 和 .Selection 属性管理上的所有错误,这让事情变得非常混乱。

对于这样的多选择网格,我建议总是使用非官方的 VCL 组件而不是 TStringGrid,如果我不记得不好,它被称为 TMultiSelectStringGrid,在它上面你有一个 .Selected 属性,每个单元格、行和列是读取/能写。当您想要Ctrl按下键进行多选时,它真的很有效,也适用于多行选择和多列选择...只需在行、列或单元格上循环以检查女巫被选中和女巫那些不是。它还有一个属性.RightMouseSelect(如果我不记得不好的话)可以右键单击选择,而且效果很好。

警告,并非所有右键单击时选择的代码都是正确的...其中很多都不关心您是否有多项选择...永远不要设置.Row是否要选择多行,只需检查如果鼠标在更改之前位于选择之外,.Selection否则右键单击可以更改选择...那么如何弹出菜单超过一行(使用示例:弹出菜单条目称为删除多个条目同时时间)。

换句话说(我真正用于 VCL 标准 TStringGrid 的代码):

procedure TYourForm.YourGridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
   ACol,ARow:Integer;
begin
     YourGrid.MouseToCell(X,Y,ACol,ARow);
     if goRowSelect in YourGrid.Options
     then begin // TStingGrig with full row selected, no individual cell must be selected
               if  (ARow<YourGrid.Selection.Top)
                 or
                   (YourGrid.Selection.Bottom<ARow)
               then begin // Where clicked is outside the actual rows that are selected
                         YourGrid.Row:=ARow;
                    end;
          end
     else begin // TStingGrig where individual cells can be selected
               if  (ARow<YourGrid.Selection.Top)
                 or
                   (YourGrid.Selection.Bottom<ARow)
                 or
                   (ACol<YourGrid.Selection.Left)
                 or
                   (YourGrid.Selection.Right<ACol)
               then begin // Where clicked is outside the actual selection
                         YourGrid.Selection:=TGridRect(Rect(ACol,ARow,ACol,ARow)); // Select the clicked cell 
                    end;
          end;
end;

注意:我确实在单元上的程序上有该代码,并调用该程序传递 Grid 引用和 X、Y 坐标;说实话,我使用的单元是type TStringGrid=class(Grids.TStringGrid)TStringGrid 的完整破解,声明为 as ,因此我可以使用视觉设计并在所有单元上拥有额外的功能;只需确保 hack 可以interface uses在单元列表末尾的部分添加此类单元(或至少在网格之后,从不在网格之前)。

控制弹出菜单的特殊提示已显示或未显示以及要显示的弹出菜单:

  • 在 event 上做OnMouseDown,从不做OnMouseUp,也不做OnClick,等等;或弹出菜单将在选择更改之前显示......并且在选择更改时(通过代码)弹出菜单将立即隐藏。
  • 如果完成了OnMouseDown不需要通过代码强制显示弹出菜单,它会正常执行;还有更多,您可以取消要显示的弹出窗口,例如,如果在单元格数据之外单击,或者也可以为 FixedCols、FixedRows 提供不同的弹出窗口,对于每个单元格,您也可以有不同的弹出窗口(我谈论设计时弹出窗口,您还可以在显示之前动态创建弹出条目等),将您想要做的代码放在 event 上OnMouseDown,谈论在设计时创建的 opoup 菜单;如果 popup 是在运行时创建的,请考虑相同:显示或不显示 on ,在自己的菜单事件OnMouseDown上构建菜单。OnPopup

基本技巧是对事件进行选择更改OnMouseDown,它在弹出菜单显示之前被触发。

啊,在我的代码上,我不介意单击了哪个按钮,因为如果在选定的多行内单击左键,它也会正常工作(我的代码不会对选择进行任何更改,也不会对行等进行任何更改。它真的什么也没做,请参见ifs),但您会看到选择仅更改为一行。

请注意,选择也可以通过鼠标左下,持续,然后移动鼠标并抬起左键来更改为多选,这将选择多个单元格/行。用户必须进行选择的所有这些方式,使得标准组件的内部实现变得如此错误,在内部编码时并未考虑到所有操作组合。

尝试在按下鼠标左键时按下Ctrland 或在标准网格上移动鼠标,完全没有代码,在 OnMouseMove 上的 ecetp 代码显示,和, 会看到你认为不可能的事情。我看到有一次告诉价值是几百万(一个不可能的价值,因为网格只有几列),相同的(与'.Col'失败时不同的价值)。Shift.Row.Col.Selection.···.Col.Row

所以不要相信返回的值.Row.Col如果你想得到什么是选择(错误的概念,它们表达了哪个单元格有焦点,与选择是什么无关);但是您可以使用它们来选择一个且仅一个行或列(仅当使用允许它的黑客网格时才选择列,或者使用带有goRowSelect=False并自己模拟列选择的网格)。

并且请始终牢记这一点(请始终这样做):

  • 具有焦点的单元格(.Row并且.Col可能会告诉您哪个是)可以在实际选择之外,是的,它可以在外面(其中很难强制失败,它会发生,无需编写任何代码,只需使用鼠标移动单击和与Alt, Ctrl, Shift, 光标的组合并Spacebar可能发生)。所以不要相信.Row也不.Col知道关于选择的任何事情(这在概念上是错误的),总是使用.Selection.···.

希望这有助于在我理解这一点之前不要让我头疼:

  1. .Selection.···仅代表选定的一个矩形单元格区域(不介意goRowSelect是真还是假)。
  2. .Selection:=TGrigRect(Rect(Left,Top,Right,Bottom));是将实际选择更改为另一个的最佳方法(由您自己确保,Left<=Right否则Top<=Bottom事情可能会变得非常糟糕);把代码想象成这么大:(.Selection:=TGrigRect(Rect(Min(Left,Right),Min(Top,Bottom),Max(Right,Left),Max(Bottom,Top));Minand Max,它们是Maths单位的)。
  3. .Row给出具有虚线矩形的特殊单元格的行号,无论它是在选择内还是在选择外,如果没有单元格有焦点,也可以是 -1。它与选择无关。
  4. .Col给出绘制了一个虚线矩形的特殊单元格的 col 编号,无论它是在选择内部还是在选择外部,如果没有单元格有焦点,也可以是 -1。它与选择无关。

了解这四件事,我可以得到一些过去的东西。我花了太多时间才理解这两个不同的概念:具有焦点的单元格('.Row' 和 '.Col')和选定的单元格(.Selection.···)。

PD:请随意分享这些信息!

于 2016-09-30T11:18:48.797 回答
0

在我的一个基于 TStringGrid 的控件中,我使用 MouseDown/MouseUp 事件来处理该弹出菜单,因为我有两个不同的上下文菜单,具体取决于您单击了 TStringGrid 的哪个区域。奇迹般有效。只要确保在代码之前调用继承即可。

--
请注意,当您弹出上下文菜单时,调用事件的顺序有些奇怪。更准确地说,当您按下 RMB 并弹出弹出菜单时,不会立即调用 MouseUp 事件。下次按下鼠标按钮(任何按钮)时会调用它。

另请参阅:TStringGrid - OnMouseUp 未被调用!

于 2010-08-19T19:59:30.417 回答
0

嗯...我无法在我的 D2010 中复制该问题。

一个快速的想法是,可能是因为您没有选择任何行而出现问题?会在表单的 OnCreate 帮助中首先将 StringGrid 的 Row 设置为 0 吗?

于 2010-08-22T05:11:41.313 回答
0

如果您想要/需要右键单击一个单元格将焦点移至它(通常通过左键单击完成),您可以使用我使用的代码:

type TStringGridHacked=class(Grids.TStringGrid); // This is to have access to hidden (and very usefull) methods, like MoveCol, MoveRow, FocusCell, etc
procedure TMyForm.TheStringGridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
   ACol,ARow:Integer;
begin
     if mbRight=Button
     then begin // Right mouse button clicked
               TheStringGrid.MouseToCell(X,Y,ACol,ARow); // Convert X,Y coordinates of mouse to cell Col & Row
               if (FixedCols<=ACol)and(FixedRows<=ARow)
               then begin // Cell is not a header one
                         TStringGridHacked(TheStringGrid).FocusCell(ACol,ARow,True); // Send focus to such cell doing only one move, so triggering SelectCell only once
                    end;
          end;
end;

自从我在互联网上找到它以来,我已经使用该 Hacktype TStringGridHacked=class(Grids.TStringGrid)很长时间了。

我在实现使用之后放置了这样的hack(类型声明)(如果我在同一个单元上多次需要它),或者在程序之前的代码中(如果我只需要一次);两种方式都可以正常工作并使代码更清晰。

于 2015-02-04T13:47:13.087 回答