Jus*_*ade 4 delphi mouseevent custom-component
通过长按,我的意思是按下按钮/面板并保持一段时间(比如2秒)而不释放或拖动.它在手机和触摸设备中很常见.
我曾尝试使用Gesture,在TabletOptions中检查了PressAndHold并在InteractiveGestureOptions中检查了所有内容,但是长时间按下不会导致OnGesture调用.
我能想到的另一种实现增加一个计时器,在开始的MouseDown,并结束它在任何计时器所触发,的startDrag,的MouseUp或鼠标离开.然而,正如我希望这个行为添加到几个不同的按键和面板组件,我将不得不重写程序的早午餐每个类和各地复制代码为每个组件.
有没有更好的方法来实现这一目标?
编辑:
致NGLN
哇,伟大的工作!结合您对这些滚动效果的回答,VCL几乎可以实现移动操作系统的外观和感觉!
您的代码与常用控件完美配合,但在我的案例中我遇到了2个问题
我有一些自定义按钮,它有一些禁用的HTML标签(标题,标题,页脚)覆盖标签原始表面,使用你的代码,FChild将是其中一个标签,但它不会得到MouseCapture.我添加以下行来克服它:
而不是TControlAccess(FChild).Enabled做FChild:= FChild.Parent;
最后,对于一些更复杂的控件,如TCategoryButtons或TListBox,事件的用户可能需要检查不是针对整个控件而是检查控件中的指定项.所以我认为我们需要保存原来的CursorPos并在定时器触发时触发另一个事件,以便手动确定它是否符合长按条件.如果是或未分配事件,则使用您的默认代码进行确定.
总而言之,我们可以创建一个支持LongPress的表单/面板来托管所有其他控件.这比使用Component by Component实现LongPress功能要容易得多!十分感谢!
编辑2:
致NGLN
再次感谢您的组件版本,这是一种很好的方法,不需要对现有组件进行任何修改,并且可以检测到长按!
为了您的信息,我做了一些修改,以满足自己的需要.
再次感谢您的出色工作.
NGL*_*GLN 12
在每个鼠标左键单击,WM_PARENTNOTIFY发送到所点击控件的所有(大)父母.因此,这可以用于跟踪长按的起始点,并且可以使用计时器来定时按压的持续时间.剩下的就是决定何时应将印刷机称为长按.当然,将这一切包装在一个很好的组件中.
在下面编写的组件中,OnLongPress满足以下条件时会触发事件处理程序:
Mouse.DragThreshold.关于代码的一些解释:
OnMouseUp事件处理程序,否则连续点击也可能导致长按.中间事件处理程序禁用跟踪计时器,调用原始事件处理程序并将其替换回来.FindControlAtPos例程,可以在任意窗口上执行深度搜索.替代方案是(1)TWinControl.ControlAtPos,但它只搜索一个深度,和(2)Controls.FindDragTarget,但尽管AllowDisabled参数,它无法找到禁用的控件.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.
Run Code Online (Sandbox Code Playgroud)
要使此组件正常工作,请将其添加到包中,或使用此运行时代码:
...
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;
Run Code Online (Sandbox Code Playgroud)