При работе с модальными окнами в Delphi разработчики часто сталкиваются с проблемой: стандартные события мыши (OnMouseDown, OnMouseUp) не срабатывают, когда пользователь кликает за пределами модальной формы. Как отмечает пользователь Squall_FF8, попытки использовать MouseMove возвращают координаты <-1,-1>, что не является надежным индикатором, так как такие же значения могут возвращаться при других операциях, например, при изменении размера окна.
Решение с использованием Windows Hooks
Один из предложенных вариантов решения - использование системных хуков Windows. Пользователь limelect предоставил пример кода, демонстрирующий установку хука для отслеживания событий мыши:
function MouseHook(Code: integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
ClientPt: TPoint;
begin
if (GlobData <> nil) and
(Code = HC_ACTION) and
((wParam = WM_MOUSEMOVE) or (wParam = WM_NCMOUSEMOVE)) then
begin
with PMouseHookStruct(lParam)^ do
begin
PostMessage(GlobData^.ActiveHandle, WM_APP+2, Pt.X, Pt.Y);
ClientPt := Pt;
ScreenToClient(hwnd, ClientPt);
PostMessage(GlobData^.ActiveHandle, WM_APP+3, ClientPt.X, ClientPt.Y);
end;
end;
Result := CallNextHookEx(0, Code, wParam, lParam);
end;
function InstallMouseHook(Wnd: HWND): Boolean; stdcall;
begin
Result := False;
if (GlobData = nil) then Exit;
if (GlobData^.THook = 0) then
begin
GlobData^.THook := SetWindowsHookEx(WH_MOUSE, @MouseHook, HInstance, 0);
if GlobData^.THook = 0 then Exit;
end;
GlobData^.ActiveHandle := Wnd;
Result := True;
end;
function UninstallMouseHook: Boolean; stdcall;
begin
Result := True;
if (GlobData = nil) then Exit;
if (GlobData^.THook <> 0) then
begin
if not UnhookWindowsHookEx(GlobData^.THook) then Exit;
GlobData^.THook := 0;
end;
GlobData^.ActiveHandle := 0;
Result := True;
end;
Для использования этого кода вам потребуется определить структуру GlobData:
type
PGlobalData = ^TGlobalData;
TGlobalData = record
THook: HHOOK;
ActiveHandle: HWND;
end;
var
GlobData: PGlobalData;
Альтернативное решение с проверкой активного окна
Если вам нужно только определить, что пользователь кликнул вне модального окна (без точных координат), можно использовать более простой подход:
procedure TModalForm.FormDeactivate(Sender: TObject);
begin
// Закрываем модальное окно при потере фокуса
Close;
end;
Или более сложный вариант с проверкой координат:
procedure TModalForm.FormCreate(Sender: TObject);
begin
// Установка таймера для проверки позиции курсора
Timer := TTimer.Create(Self);
Timer.Interval := 100;
Timer.OnTimer := CheckMousePosition;
Timer.Enabled := True;
end;
procedure TModalForm.CheckMousePosition(Sender: TObject);
var
MousePos: TPoint;
begin
MousePos := Mouse.CursorPos;
if not PtInRect(BoundsRect, MousePos) then
begin
// Курсор вне модального окна
// Можно добавить дополнительную логику здесь
end;
end;
Кросс-платформенное решение для FMX
Если ваш проект использует FireMonkey (FMX) и должен работать на разных платформах (Windows, Android, iOS), системные хуки Windows не подойдут. В этом случае можно использовать следующий подход:
procedure TModalForm.FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
Shift: TShiftState);
begin
// Обработка нажатия Escape для закрытия окна
if Key = vkEscape then
Close;
end;
procedure TModalForm.TouchOutside(Sender: TObject; const Point: TPointF);
begin
// FMX-специфичное событие для кликов вне формы
Close;
end;
Для FMX также можно использовать прозрачный фоновый прямоугольник, который будет перехватывать клики:
Выбор метода обработки кликов за пределами модального окна зависит от ваших конкретных требований:
Windows Hooks - наиболее мощное решение для VCL приложений, но требует осторожности при реализации.
Проверка активного окна или позиции курсора - проще в реализации, но может быть менее надежным.
FMX-специфичные решения - необходимы для кросс-платформенных приложений.
Для большинства случаев в VCL-приложениях рекомендуется использовать либо системные хуки (если нужны точные координаты), либо обработку события FormDeactivate (если достаточно просто определить клик вне окна). В FMX-приложениях лучше использовать встроенные механизмы вроде TouchOutside или прозрачный фоновый элемент.
Обработка кликов вне модального окна в Delphi с использованием системных хуков или проверки активного окна.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.