我在Delphi 7中使用TStringGrid有一个怪异的行为。
如果将弹出菜单与网格关联,则Delphi不会调用OnMouseUp事件。基本上,当按下RMB时,菜单的弹出会以某种方式取消/延迟OnMouseUp。实际上,为了达到100%的准确度,下次您按下鼠标按钮时,将两次调用OnMouseUp-一次是针对当前事件,一次是针对丢失/延迟的事件。

这将破坏程序的整个逻辑,因为当用户按下鼠标按钮时,下一次将调用不需要的代码。

最佳答案

上下文菜单的自动弹出是对鼠标右键单击的响应。相同的点击也会触发OnMouseUp事件。 VCL开发人员可以选择在显示弹出窗口之前或之后触发“ OnMouseUp”事件。显然后者是有效的,即在关闭弹出窗口时触发该事件(通过鼠标或键盘,例如按“ Esc”键)。

事件没有加倍,当您按下向左按钮以关闭弹出窗口时,您通过释放向左按钮再次触发“ OnMouseUp”事件。

您有几种选择。一种是派生一个新类并重写MouseDown方法来触发您自己的事件。一个例子;

type
  TMyStringGrid = class(TStringGrid)
  private
    FOnRButtonUp: TMouseEvent;
  protected
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
  published
    property OnRButtonUp: TMouseEvent read FOnRButtonUp write FOnRButtonUp;
  end;
[...]

procedure TStringGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  if (Button = mbRight) and Assigned(FOnRButtonUp) then
    FOnRButtonUp(Self, Button, Shift, X, Y);
  inherited;
end;


另一种选择是处理VM_RBUTTONUP消息。这可以通过如上所述派生一个新类或替换网格的WindowProc来完成。在此here中有一个替换WindowProc question的示例。

另一种选择是不让鼠标上移事件留下来,而是在弹出菜单的OnPopup事件中进行处理。在显示弹出窗口之前会触发此事件。您可以使用Mouse.CursorPos获取鼠标坐标。

另外,另一种选择是将弹出菜单的AutoPopup属性设置为False,并在OnMouseUp事件(或者更好的是在OnContextMenu事件中)首先进行一些处理,然后显示弹出窗口。一个例子;

procedure TForm1.StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  Pt: TPoint;
begin
  // Do processing

  if Button = mbRight then begin
    Pt := (Sender as TStringGrid).ClientToScreen(Point(X, Y));
    PopupMenu1.Popup(Pt.X, Pt.Y);
  end;
end;

10-04 18:39