Jak wykryć najechanie myszką na naszą aplikację (formę)

0

Witam jak wykryć w naszej aplikacji najechanie kursorem na naszą formę tzn. Forma ma wymiary np 500x300 pikseli i z pulpitu najeżdżam myszką na formę. Oraz opuszczenie naszej formy (tzn aplikacji)

**UWAGA !!! ** Nie chodzi mi o zdarzenie onMouseEnter, Move, Leave itp. które nie zawsze działa jak np. szybko opuścimy kursorem formę lub gdy na formie są inne wizualne komponenty (przykrywają zdarzenia dla formy głównej).

Sądzę że należy założyć jakiegoś hooka na komunikat od windows.

0

Dokłądnie. Potrzebujesz globalnego hook na myszkę. A konkretnie na WH_MOUSE_LL (która to stała wyniosi 14). Dokładny opis jest do wygooglowania pod hasłem Hooks Overview - pierwszy link na MSDNie. A to czy dany punkt z koordynatami myszki jest w obszarze jakiejś formatki można sobie łatwo sprawdzić na przykład taką funkcją jak ta:

function IsPointInForm(Pt : TPoint; AForm : TForm) : boolean;
begin
  Result := (Pt.X >= AForm.Left) and (Pt.X <= AForm.Left + AForm.Width)
    and (Pt.Y >= AForm.Top) and (Pt.Y <= AForm.Top + AForm.Height);
end;

Oczywiście przed założeniem Hooka, jeśli już interesuje Ciebie czy myszka jest w danym obszarze, należy pobrać pozycję kursora myszy przez funkcję WinAPI o nazwię GetCursorPos. Myślę, że tyle informacji wystarczy i już dalej sobie ze wszystkim poradzisz. A i na koniec dodam, że oczywiście Hook musi być globalny (stąd ten low levelowy), ponieważ lokalny nie wykrył by ruchów poza formatką.

Innym rozwiązanem jest jeszcze w wątku z krótkim Sleepem lub TImerze z krótkim Intervalem sprawdzać powyższą funkcją, którą podałem przez porównywanie GetCursorPos czy myszka jest w określonym obszarze formatki. Jednak myślę, że do takich zastosowań lepsze i mniej obciążające bedzie użycia właśnie hooka na WH_MOUSE_LL.

0

Mam pewien problem, stosuję procedurę PtInRect jednak po wyświetleniu okna modalnego i jego zamknieciu coś się sypie z tym PtInRect poniżej troszkę kodu i opis:

procedure TFrameMagazyn.SelectMenu_MagazynMouseLeave(Sender: TObject);
var
  R : TRect;
begin
  GetWindowRect(SelectMenu_Magazyn.Handle, R);
  if not PtInRect(R, Mouse.CursorPos) then ShowSelectMenu_Magazyn(False); 
end;

procedure TFrameMagazyn.SelectMenuButton_MagazynMouseEnter(
  Sender: TObject);
begin
  ShowSelectMenu_Magazyn(True);
end;

{Pokazuje i ukrywa zakładkę SelectMenu_Magazyn}
procedure TFrameMagazyn.ShowSelectMenu_Magazyn(AVisible: boolean);
begin
  SendMessage(Self.Handle, WM_SETREDRAW, 0, 0);                
  SelectMenu_Magazyn.Left := SelectMenuButton_Magazyn.Left;                       
  SelectMenuButton_Magazyn.Visible := not AVisible;
  SelectMenu_Magazyn.Visible := AVisible;
  SendMessage(Self.Handle, WM_SETREDRAW, 1, 0);
  RedrawWindow(Self.Handle, nil, 0, RDW_ALLCHILDREN or RDW_ERASE or RDW_INVALIDATE);
  Self.Repaint;
end;

Opis działania: jak najadę myszką na button (SelectMenuButton_Magazyn) to pokazuje się panel (SelectMenu_Magazyn) jak opuszczę panel to ukrywa się i zostaje button.
Generalnie wszystko działa z małym wyjątkiem. jak wyświetli się panel i wywołam jakieś okno modalne np showmessage to po zamknięciu panel jest widoczny (nie wykrywa wyjścia myszki poza panel - ok to rozumiem dlaczego tak się dzieje, ale jak najadę myszką na panel i wyjdę z jego obszaru to procedura MouseLeave / PInRect powinna zadziałać a tak się nie dzieje może tylko 2 razy zadziałała. Natomiast jak panel jest widoczny i wywołam taką sekwencję to wszystko wraca do normy, tak mi się wydaje:

  SendMessage(Self.Handle, WM_SETREDRAW, 0, 0);                
  SendMessage(Self.Handle, WM_SETREDRAW, 1, 0);
  RedrawWindow(Self.Handle, nil, 0, RDW_ALLCHILDREN or RDW_ERASE or RDW_INVALIDATE);

Co robię źle?

0

Nie wiem co mogę Tobie więcej doradzić. Tak na prawdę to chyba nigdy nie chciałem i nie byłem zmuszony aby w swoich programach korzystać z TFrame. Pytanie dotyczyło formatki, która standardowo metod do wykrycia wejścia i wyjścia z jej obszaru myszką nie posiada. Przynajmniej w Delphi 7. Posłużyłem się więc globalnym hookiem na myszkę. A teraz na szybko sprawdziłem, czy przy pokazaniu nowej formatki coś się sypie. Jednak kod nadal działa - u mnie - ok. Za pewne ktoś inny coś lepszego tutaj Tobie doradzi, kto korzystał z TFrame i wie jak można rozwiązać Twój problem.

0

Zamieniłem to:

 SendMessage(Self.Handle, WM_SETREDRAW, 0, 0);                
  SendMessage(Self.Handle, WM_SETREDRAW, 1, 0);
  RedrawWindow(Self.Handle, nil, 0, RDW_ALLCHILDREN or RDW_ERASE or RDW_INVALIDATE);

na to:

 
  SendMessage(Form1.Handle, WM_SETREDRAW, 0, 0);                
  SendMessage(Form1.Handle, WM_SETREDRAW, 1, 0);
  RedrawWindow(Form1.Handle, nil, 0, RDW_ALLCHILDREN or RDW_ERASE or RDW_INVALIDATE);

Czyli zamiast odwoływać się do Ramki po przez Self odświeżam formę główną i wygląda na to że wszystko działa, chociaż nie do końca jestem pewien że to jest poprawne

1 użytkowników online, w tym zalogowanych: 0, gości: 1