【问题标题】:Custom OnMouseDown that won't brake the click不会阻止点击的自定义 OnMouseDown
【发布时间】:2012-03-15 09:09:13
【问题描述】:

好的,我有一个返回TMouseEvent类型的函数

我需要执行返回的 TMouseEvent 但我不知道如何。

返回事件的简单函数:

function OMDold(obj: TObject): TMouseEvent
  begin
  ... //some operations on obj 
  result := obj.OnMouseDown; //there is casting necessary, I skip it for simplify
end; 

当前事件设置为 OMDnew,如下所示:

procedure TfmAPRBasedForm.TSDragEvent(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if something then dosomething
  else
    begin
      Sender.OnMouseDOwn := OMDold // OMDold in most cases returns null but its ok I just want to clear custom event from the object 
      //line below is a point of my question - the one I used doesnt work
      TButton(Sender).OnMouseDown(Sender,Button,Shift,X,Y) //this line throws Access viloation at me
    end;
end;

我想要达到的目标:

  1. 获取按钮默认 OnMouseDown 事件并将其存储在一些记录数据中

  2. 将 ONMouseDown 事件更改为自定义

  3. 在自定义事件过程中,有一个条件 - 如果鼠标按下是拖动,我执行拖动代码,如果不是,我会继续普通点击

  4. 要使用普通点击来触发,我想恢复默认事件并重新启动它,以便可以执行点击

就是这样

【问题讨论】:

  • 你为什么不直接打电话给OMDnew。你可以做到的。
  • 直接调用OMDnew(sender, button, shift, x,y)有什么问题?
  • 我的描述不够清楚。 Justmade可能只是给了我正确的答案。但是,我编辑了我的帖子,以供进一步使用:)
  • 如你所说,Sender.OnMouseDOwn := OMDold 通常返回 nil(非 null)。这意味着 Sender.OnMouseDOwn 也为零。这就是为什么你从调用它获得 AV 的原因。您需要在调用 TButton(Sender).OnMouseDown 之前添加 If Assigned(Sender.OnMouseDOwn) then 以便在 Sender.OnMouseDown 为空时跳过它。
  • 我已经回答了你原来的问题,虽然它可能没有真正解决你的问题,因为你没有清楚地提出你的问题。不断改变问题并不是获得更多帮助的好方法。如果您的新问题确实与原来的问题有很大不同,那么您最好提出一个新问题。

标签: delphi events


【解决方案1】:

直接通过参数调用即可:

procedure TfmAPRBasedForm.TSDragEvent(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if something then dosomething
  else
    begin
      TButton(Sender.OnMouseDOwn) := OMDold // OMDold in most cases returns nil (not null) but its ok I just want to clear custom event from the object 
      //line below is a point of my question - the one I used doesnt work
      If Assigned(TButton(Sender).OnMouseDown) then // Check if there is really an TMouseEvent
        TButton(Sender).OnMouseDown(Sender,Button,Shift,X,Y) //Call only when Event exist
    end;
end;

您可能需要更改 Button、Shift、X、Y,但如果您在 OMDold 中使用它们并且您需要在 dragEvent 中使用除当前值之外的值,例如删除 ssShift 左右。

如果您的 OMDold 存储为 TMethod,那么您可以使用:

TMouseEvent(OMDOld)(Sender,Button,Shift,X,Y);

下面是一个完整的测试示例,经过调整以显示您希望以类似方式实现的目标:

Unit Unit4;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm4 = class(TForm)
    btn1: TButton;
    btn2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure btn2MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private declarations }
    procedure NewMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  public
    { Public declarations }
    OMDold : TMouseEvent;
    IsNew : Boolean;
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

procedure TForm4.FormCreate(Sender: TObject);
begin
  OMDold := btn1.OnMouseDown;
  btn1.OnMouseDown := NewMouseDown;
  IsNew := True;
end;

procedure TForm4.NewMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if IsNew then
    ShowMessage('New Method!')
  else if Assigned(OMDold) then
    OMDold(Sender,Button,Shift,X,Y);
end;

procedure TForm4.btn1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  ShowMessage('Original Method!');
end;

procedure TForm4.btn2MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  IsNew := not IsNew;
end;

end.

【讨论】:

  • 编辑了我的帖子,我不确定是不是我要找的内容
  • @JacekKwiecień 都一样,只要调用OMDOld(Sender,Button,Shift,X,Y);
  • OMDOld 可以为 Nil,因此需要额外检查。
  • 它向我抛出了访问冲突 :( 我确定 OMDold 返回正确的事件 - 我使用 OMDold(Sender)(Sender,Button,Shift,X,Y);
  • @Jacek,这意味着你的 OMDold 什么都没有。例如,您通过 OMDold := button1.OnMouseDown 分配了 OMDold。但是,button1 在那一刻没有 OnMouseDown 事件,然后你什么也不分配给 OMDold 并且它会导致返回 false 给 Assign 并在使用时导致 AV。
【解决方案2】:

阻止 OnMouseDown 运行 CLick 事件的示例过程:

procedure TForm1.CustomowyEvent(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
if (DragDetectPlus(TWinControl(Sender))) then ShowMessage('detected drag!')

else
  begin
    TButton(Sender).OnMouseDown := DME;
    TButton(Sender).OnClick(Sender);
  end;
end;

TButton(Sender).OnClick(Sender) 运行点击,所以基本上如果我们不想拖动它就会点击。

【讨论】:

  • 嗯,这是另一个问题,然后是您的问题“使用 TMouseEvent 类型从代码调用事件”:)
  • 如果我看到DragDetectPlus,只需一个离题说明。我希望这个函数不仅仅是检查控件是否被拖动,因为如果没有,最好使用Dragging 函数。
  • 具有每个TControl 后代的那个。如果Dragging 返回True,则控件正在被拖动。这是您的 DragDetectPlus 函数名称的旁注,所以我想知道它实际上是做什么的 ;-)
【解决方案3】:

分别解决所有问题部分:

  1. 获取按钮默认OnMouseDown事件并将其存储在一些记录数据中

    在表单声明中添加一个私有字段并将旧事件分配给它:

      private
        FOldButtonOnMouseDown: TMouseEvent;
      end;
    
    ...
    
      FOldButtonOnMouseDown := Button.OnMouseDown;
    
  2. OnMouseDown 事件更改为自定义

    将您的自定义事件处理程序分配给OnMouseDown 属性:

      private
        procedure NewButtonMouseDown(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);
      end;
    
    ...
    
      Button.OnMouseDown := NewButtonMouseDown;
    
  3. 在自定义事件过程中有一个条件 - 如果鼠标按下是拖动,我执行拖动代码,如果不是,我会继续普通点击

    测试是否分配了原始事件,如果是,则调用它:

    procedure TForm1.NewButtonMouseDown(Sender: TObject; 
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    begin
      if DragCondition then
        ExecuteDragCode
      else
        if Assigned(FOldButtonOnMouseDown) then
          FOldButtonOnMouseDown(Sender, Button, Shift, X, Y);
    end;
    
  4. 要使用普通点击进行,我想恢复默认事件并重新运行它以便可以执行点击

    恢复事件:

    Button.OnMouseDown := FOldButtonOnMouseDown;
    

将这一切结合在一起是您的下一个挑战。这尤其取决于拖动 stuf 的实现方式,但您可以查看this answer,其中我还临时交换了OnMouseUp 事件以撤消所有更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-10
    • 2021-12-29
    • 1970-01-01
    • 2018-03-05
    • 2012-06-14
    • 1970-01-01
    相关资源
    最近更新 更多