Delphi 向button类添加长按事件的最佳方法是什么?
长按,我指的是按下按钮/面板并保持一段时间(比如2秒),而不释放或拖动。这在手机和触摸设备中很常见 我试过使用手势,在桌面选项中选择了topress和hold,并选择了所有的InteractiveGestureOptions,但长时间按下不会导致任何感觉调用 我能想到的另一个实现是添加一个计时器,在MouseDown中启动它,在timer-Fired、StartDrag、MouseUp或MouseLeave中结束它。然而,由于我想将此行为添加到几个不同的按钮和面板组件中,我必须重写每个类中的一个早午餐过程,并复制每个组件的代码 有没有更好的方法来实现这一点Delphi 向button类添加长按事件的最佳方法是什么?,delphi,mouseevent,custom-component,Delphi,Mouseevent,Custom Component,长按,我指的是按下按钮/面板并保持一段时间(比如2秒),而不释放或拖动。这在手机和触摸设备中很常见 我试过使用手势,在桌面选项中选择了topress和hold,并选择了所有的InteractiveGestureOptions,但长时间按下不会导致任何感觉调用 我能想到的另一个实现是添加一个计时器,在MouseDown中启动它,在timer-Fired、StartDrag、MouseUp或MouseLeave中结束它。然而,由于我想将此行为添加到几个不同的按钮和面板组件中,我必须重写每个类中的一个
编辑: 到NGLN 哇,干得好!加上你对这些滚动效果的回答,VCL几乎可以实现移动操作系统的外观和感觉 您的代码与常用控件完美配合,但我遇到了两个问题
编辑2: 到NGLN 再次感谢您的组件版本,这是一个极好的方法,不需要对现有组件进行任何修改,并且可以检测到到处都有长时间的按压 供你参考,我做了几次修改以满足我自己的需要
再次感谢您的出色工作 在每次鼠标左键单击时,都会发送给所单击控件的所有(主要)父控件。因此,这可以用于跟踪长时间按压的起始点,并且按压的持续时间可以通过计时器计时。剩下的就是决定什么时候一个新闻应该被称为长新闻。当然,这一切都要用一个很好的组件来总结 在下面编写的组件中,当满足以下条件时,将触发
OnLongPress
事件处理程序:
- 间隔后,控件仍有鼠标捕获、焦点或被禁用
- 间隔结束后,鼠标移动的距离不超过
鼠标。拖动阈值
- 它临时替换控件的
事件处理程序,否则连续单击也可能导致长按。中间事件处理程序禁用跟踪计时器,调用原始事件处理程序并将其替换回来OnMouseUp
- 长按之后,活动控件被重置,因为我认为长按并不是为了聚焦控件。但这只是我的猜测,它可能是一个属性的候选人
- 还跟踪窗体本身(而不仅仅是其子窗体)上的长时间按压
- 有一个自定义的
例程,可在任意窗口上执行深度搜索。备选方案有:(1)FindControlAtPos
,但它只搜索一级深度;和(2)TWinControl.ControlAtPos
,但尽管有Controls.FindDragTarget
参数,它无法找到禁用的控件AllowDisabled
计时器将是错误的。按下鼠标上的按钮。不能在鼠标仍处于按下状态时触发动作。“秒表更合理。”大卫·费弗南,谢谢。然而,在Android桌面上,当你按下大约2秒钟时,它会变成另一种模式,让你添加快捷方式或小部件。媒体尚未发布,但我认为此后不会有点击/鼠标事件。我认识安德烈
unit LongPressEvent;
interface
uses
Classes, Controls, Messages, Windows, Forms, ExtCtrls;
type
TLongPressEvent = procedure(Control: TControl) of object;
TLongPressTracker = class(TComponent)
private
FChild: TControl;
FClickPos: TPoint;
FForm: TCustomForm;
FOldChildOnMouseUp: TMouseEvent;
FOldFormWndProc: TFarProc;
FOnLongPress: TLongPressEvent;
FPrevActiveControl: TWinControl;
FTimer: TTimer;
procedure AttachForm;
procedure DetachForm;
function GetDuration: Cardinal;
procedure LongPressed(Sender: TObject);
procedure NewChildMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure NewFormWndProc(var Message: TMessage);
procedure SetDuration(Value: Cardinal);
procedure SetForm(Value: TCustomForm);
procedure StartTracking;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Form: TCustomForm read FForm write SetForm;
published
property Duration: Cardinal read GetDuration write SetDuration
default 1000;
property OnLongPress: TLongPressEvent read FOnLongPress
write FOnLongPress;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TLongPressTracker]);
end;
function FindControlAtPos(Window: TWinControl;
const ScreenPos: TPoint): TControl;
var
I: Integer;
C: TControl;
begin
for I := Window.ControlCount - 1 downto 0 do
begin
C := Window.Controls[I];
if C.Visible and PtInRect(C.ClientRect, C.ScreenToClient(ScreenPos)) then
begin
if C is TWinControl then
Result := FindControlAtPos(TWinControl(C), ScreenPos)
else
Result := C;
Exit;
end;
end;
Result := Window;
end;
{ TLongPressTracker }
type
TControlAccess = class(TControl);
procedure TLongPressTracker.AttachForm;
begin
if FForm <> nil then
begin
FForm.HandleNeeded;
FOldFormWndProc := Pointer(GetWindowLong(FForm.Handle, GWL_WNDPROC));
SetWindowLong(FForm.Handle, GWL_WNDPROC,
Integer(MakeObjectInstance(NewFormWndProc)));
end;
end;
constructor TLongPressTracker.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTimer := TTimer.Create(Self);
FTimer.Enabled := False;
FTimer.Interval := 1000;
FTimer.OnTimer := LongPressed;
if AOwner is TCustomForm then
SetForm(TCustomForm(AOwner));
end;
destructor TLongPressTracker.Destroy;
begin
if FTimer.Enabled then
begin
FTimer.Enabled := False;
TControlAccess(FChild).OnMouseUp := FOldChildOnMouseUp;
end;
DetachForm;
inherited Destroy;
end;
procedure TLongPressTracker.DetachForm;
begin
if FForm <> nil then
begin
if FForm.HandleAllocated then
SetWindowLong(FForm.Handle, GWL_WNDPROC, Integer(FOldFormWndProc));
FForm := nil;
end;
end;
function TLongPressTracker.GetDuration: Cardinal;
begin
Result := FTimer.Interval;
end;
procedure TLongPressTracker.LongPressed(Sender: TObject);
begin
FTimer.Enabled := False;
if (Abs(FClickPos.X - Mouse.CursorPos.X) < Mouse.DragThreshold) and
(Abs(FClickPos.Y - Mouse.CursorPos.Y) < Mouse.DragThreshold) and
(((FChild is TWinControl) and TWinControl(FChild).Focused) or
(TControlAccess(FChild).MouseCapture or (not FChild.Enabled))) then
begin
FForm.ActiveControl := FPrevActiveControl;
if Assigned(FOnLongPress) then
FOnLongPress(FChild);
end;
TControlAccess(FChild).OnMouseUp := FOldChildOnMouseUp;
end;
procedure TLongPressTracker.NewChildMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
FTimer.Enabled := False;
if Assigned(FOldChildOnMouseUp) then
FOldChildOnMouseUp(Sender, Button, Shift, X, Y);
TControlAccess(FChild).OnMouseUp := FOldChildOnMouseUp;
end;
procedure TLongPressTracker.NewFormWndProc(var Message: TMessage);
begin
case Message.Msg of
WM_PARENTNOTIFY:
if TWMParentNotify(Message).Event = WM_LBUTTONDOWN then
StartTracking;
WM_LBUTTONDOWN:
StartTracking;
end;
with Message do
Result := CallWindowProc(FOldFormWndProc, FForm.Handle, Msg, WParam,
LParam);
end;
procedure TLongPressTracker.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (AComponent = FForm) and (Operation = opRemove) then
DetachForm;
if (AComponent = FChild) and (Operation = opRemove) then
begin
FTimer.Enabled := False;
FChild := nil;
end;
end;
procedure TLongPressTracker.SetDuration(Value: Cardinal);
begin
FTimer.Interval := Value;
end;
procedure TLongPressTracker.SetForm(Value: TCustomForm);
begin
if FForm <> Value then
begin
DetachForm;
FForm := Value;
FForm.FreeNotification(Self);
AttachForm;
end;
end;
procedure TLongPressTracker.StartTracking;
begin
FClickPos := Mouse.CursorPos;
FChild := FindControlAtPos(FForm, FClickPos);
FChild.FreeNotification(Self);
FPrevActiveControl := FForm.ActiveControl;
FOldChildOnMouseUp := TControlAccess(FChild).OnMouseUp;
TControlAccess(FChild).OnMouseUp := NewChildMouseUp;
FTimer.Enabled := True;
end;
end.
...
private
procedure LongPress(Control: TControl);
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
with TLongPressTracker.Create(Self) do
OnLongPress := LongPress;
end;
procedure TForm1.LongPress(Control: TControl);
begin
Caption := 'Long press occurred on: ' + Sender.ClassName;
end;