Skocz do zawartości

UserKontrol?


Aberration

Recommended Posts

Bardzo się zdziwiłem, gdy zobaczyłem, że do obsługi myszuży w kontrolce FlatBtn używasz timera. Nie jestem pewien, ale ilość timerów jest chyba ograniczona(?), a co będzie jeśli będziesz chciał mieć kilka FlatButtonów (a zazwyczaj tak jest)?. Według mnie lepiej było by obsługiwać WM_NCHITTEST i SetCapture(), ReleaseCapture().

Link do komentarza
Udostępnij na innych stronach

komunikat WM_NCHITTEST jest wywolywany tylko w momencie kiedy myszka jest nad oknem. Nawet SetCapture() nic nie da jak dosc szybko przesuniesz myszke poza okno glowne. I tak w tym momencie do okna juz nie zostanie przeslany komunikat i nie odblokujesz aktywnego okna (releasecapture). Byc moze sie myle, ale jezeli ty znasz rozwiazanie to napisz krotki kod jak ty to widzisz.
Zaintrygowala mnie twoja wiadomosc i chcialem sprawdzic czy to dziala. Okazuje sie, ze jak szybko usuniemy myszke poza okno glowne, jednoczesnie zjezdzajac z naszego flat buttona, do okna nie jest przesylany zaden komunikat - mimo setcapture(). I jak wtedy odblokowac aktywne okno?

Link do komentarza
Udostępnij na innych stronach

Spoon napisał:
...bo tworzenie timera jest trochę lamerskie ;)

Hehe, dzięki za uznanie ;).

Jasne, że można to zrobić na kilka sposobów. Sam miałem pewne opory przed timerem, ale ten pomysł jako pierwszy rozwiązał wszystkie moje problemy z wejścem/wyjściem myszy z obszaru kontrolki, więc go zostawiłem.

Alternatywne rozwiązanie praktycznie podał wyżej już sam Marcin.
W komunikacje WM_NCHITTEST (można też użyć WM_MOUSEMOVE):
1. Przechwytujemy ruchy myszki ( używamy SetCapture(); ).
2. Sprawdzamy, czy mysz znajduje się w obszarze kontrolki (ze względu na to, że użyliśmy SetCapture(); nie możemy być tego pewni).
3. Jeżeli tak, to ustawiamy odpowiednią zmienną (wMouseOver w przkładzie do FlatBtn) na true
4. Jeżeli nie (mysz zeszła znad obszaru kontrolki), to ustawiamy tę zmienną na false i zwalniamy przechwytywanie myszy ( ReleaseCapture(); )

Nie wiem jak to będzie z przechwytywaniem tego komunikaty przy szybkich ruchach myszy, ale uważam, że nie powinno być z tym problemów (w wolnej chwili sprawdzę).

Link do komentarza
Udostępnij na innych stronach

To teraz wyobraz sobie, ze flat button lezy na krawedzi okna glownego. Unosisz myszke nad kontrolke, zmenna przyjmuje wartosc true (w kom. np. WM_MOUSEMOVE). Teraz rusz z kontrolki myszke szybciutko poza okno glowne. Jak zmienisz zmienna na true, jak do niej nie sa juz przesylane zadne komunikaty - mimo SetCapture(). Trzeba to sprawdzic, bo kazda sytuacja musi byc obsluzona.
Ha, mozna zalozyc hooka na mysze. Ale jezeli oprocz tego, ze kursor znajdzie sie poza oknem to np. zostanie klikniety jakies inne okno, z poza naszego procesu. Wtedy hook juz tez nie bedzie funkcjonowal. Chyba, ze zalozysz globalnego hooka w dll'u o wspolnej pamieci dzielonej. Ale dla flatta takie rzeczy robic?

Link do komentarza
Udostępnij na innych stronach

No i rozwiazal sie jeden problem. Kiedys byl takie pytanie w sprawie tworzenia wlasnych klas, ale takich, zeby mogly byc wykorzystywane przez wszystkie aplikacje, tak jak np. klasa EDIT. Salwadore odpowiedzial wlasnie na ten problem.
Ale czy globalne klasy maja cos wspolnego z badanem ruchu kursora poza forma? 9_9

Link do komentarza
Udostępnij na innych stronach

Sprawdziłem w praktyce algorytm, który napisałem powyżej (użyłem komunikatu WM_MOUSEMOVE). Nie zauważyłem błędnego działania kontrolki przy szybkim zdejmowaniu myszy z przycisku. Problem pojawia się natomiast w momencie kliknięcia przycisku "Kontakt", kiedy to otwiera się okno domyślnego klienta poczty i zasłania przycisk. Po ponownym wyświetleniu okna programu testowego przycisk cały czas jest wypukły (do ponownego naciśnięcia go).

Marcinie, rozwiń proszę swój pomysł i napisz jak, według Ciebie, należało by go zrealizować.

Salwadore, dzięki za cenną uwagę. Dodam tylko, że w przypadku użycia tego stylu, po skończeniu używania kontrolki należy jej klasę "odrejestrować" funkcją UnregisterClass();.

Ps.

Marcin napisał:
...ilość timerów jest chyba ograniczona...

Rozumiem, że jest to ograniczenie dla całego systemu? Czy ktoś w związku z tym orientuje się ile maksymalnie timerów system może obsłużyć? (Wrzuciłem do aplikacji 30 przycisków i wszystko działało jak należy - dla większej ilości nie sprawdzałem.)

Link do komentarza
Udostępnij na innych stronach

Wkleiłem Twoją funkcję obsługi (żywcem) do kodu kontrolki i...

1.

Salwadore napisał:
DeleteObject ((HGDIOBJ) hfFlateButton);

Wywołując tę funkcję dla hfFlateButton kasujesz obiekt czcionki, z którego korzysta cały dialog box i wszystkie jego kontrolki-dzieci (w związku z tym przy kolejnym odmalowaniu okna czcionka zmiania się na standardową - tę dużą, pogrubioną). Ogólnie uważam, że nie powinno się kasować obiektów, których się nie stworzyło.

2. Przycisk rzeczywiście wraca do stanu pierwotnego po zasłonięciu go klientem poczty, ale niestety dopiero po najechaniu myszą na okno-rodzica FlatButtona.

//edit
3. Ustaw przycisk tak aby jego krawędź stykała się z krawędzią okna, albo tak aby trochę za nią wychodził. Najedź na przycisk i zjedź z niego zjeżdżając jednocześnie z okna-rodzica. Przycisk pozostaje wypukły.

Link do komentarza
Udostępnij na innych stronach

Dzięki za słuszne uwagi, błedy poprawię.

1. Z przyzwyczajenia kasuje wszystkie niepotrzebne obiekty.

2. Postaram się znaleść rozwiązanie inne niż timer.

3. Błąd powstaje wtedy, gdy sprawdza się pozycje kursora w jednym prostokącie zamiast dwóch (rectParent & rectClient).

+---------------------------------+
|                                 |
|                                 |
|                         +-------+------+
|                         |              |
|          rectParent     |  rectClient  |
|                         |              |
|                         +-------+------+
|                                 |
|                                 |
+---------------------------------+
Link do komentarza
Udostępnij na innych stronach

I wlasnie o to caly czas chodzi, o kontrolke bedaca na krawedzi okna glownego. Szybki ruch myszki powoduje, ze okno juz nie obsluzy komunikatu (prawdopodobnie zadnego - nie jestem do konca pewien) jak kursor bedzie poza forma glownego okna. Ja nie widzialem innego wyjscia jak zastosowac zegar. Chyba, ze tworzy sie kontrolki globalne, jak salwadore podpowiedzial. W dll'ach mozna utworzyc hooka "maksymalnie" globalnego (w dllu musi byc zmienna wspoldzielaca miedzy wszystkie procesy - salwadore juz zadal to pytanie w jednym poscie), ktory by sprawdzal polozenie myszki. Wtedy to juz by byl luks.

Link do komentarza
Udostępnij na innych stronach

Poprawiłem, jak napisałem, żrby sprawdzało czy kursor jest na przycisku i w oknie rodzica, to błedu nie wykazuje:

                case WM_MOUSEMOVE:          
                        lData = GetWindowLong (hWnd, GWL_USERDATA);
                        GetCursorPos (& ptCursor);
                        GetWindowRect (hWnd, & rectClient);
            if (GetCapture () != hWnd)
                                SetCapture (hWnd);
                        RECT rectParent;
                        GetWindowRect (GetParent (hWnd), & rectParent);
                        if (PtInRect (& rectParent, ptCursor) && PtInRect (& rectClient, ptCursor))
                        {
                if (HIWORD (lData) == false)
                                {
                                        GetClientRect (hWnd, & rectClient);
                                        InvalidateRect (hWnd, & rectClient, true);
                                }
                                SetWindowLong (hWnd, GWL_USERDATA, MAKELONG (LOWORD (lData), true));
                        }
                        else
                        {
                                SetWindowLong (hWnd, GWL_USERDATA, MAKELONG (LOWORD (lData), false));
                                if (GetCapture () == hWnd) //&& LOWORD (lData) == false)
                                        ReleaseCapture ();
                                GetClientRect (hWnd, & rectClient);
                                InvalidateRect (hWnd, & rectClient, true);
                        }
                        break;
Link do komentarza
Udostępnij na innych stronach

Nie musi umieszczć poza. Wystarczy jak przycisk będzie się stykał z krawędzią okna, albo jak przycisk będzie trochę dalej ale akurat będzie dużo obliczeń i komunikaty będą wysyłane rzadziej. Z resztą tak samo dzieje się jak przysłonisz przycisk innym oknem.
Poza tym wykonaj sobie jeszcze taki eksperyment:
Zrób sobie przycisk który nic nie otwiera (powiedzmy niech zmienia tekst okna). Najedź na niego, naciśnij klawisz myszy i puść bez poruszania myszką.

Link do komentarza
Udostępnij na innych stronach

  • 3 weeks later...

Marcin came back!!!

Po pierwsze to sorry, że tak długo nie odpisywałem, ale nie mam stałego łącza, a ostatnio nawet przez phone mi co trochę rozłączało. Ale mam dla cierpliwych rozwiązanie.

Po drugie to moje pierwsze wersja (ta z SetCapture) była zła. Dział ona poza obszarem okna tylko wtedy, gdy jakiś przycisk mysz jest pressed.
A po za tym WM_NCMOUSEMOVE nie ma nic to rzeczy/

A po trzecie:

TRACKMOUSEEVENT tme;
        case WM_MOUSEMOVE:
                if(firsttime){
                        firsttime = false;
                        GetClientRect(okno,&rect);
                        tme.cbSize = sizeof(tme);
                        tme.dwFlags = TME_LEAVE;
                        tme.dwHoverTime = 0;
                        tme.hwndTrack = okno;
                        TrackMouseEvent(&tme);
                        InvalidateRect(okno,0,0);
                }
                return 0;
        case WM_MOUSELEAVE:
                firsttime = false;
                InvalidateRect(okno,0,0);
                return 0;

Zmienna firsttime oznacza, czy najście na mysz jest po raz pierwszy. (Od niej zależy też wygląd buttona). Ważne jest także nowe dodane przezemnie rzeczy:WM_MOUSELEAVE i TrackMouseEvent().

TrackMouseEvent() jest odpowiedzialna za sprawdzanie (przy podanych parametrach) czy myszka opuściła obszar kontrolki i jeśli tak to wysyła do niej WM_MOUSELEAVE. Parametrem jest wskaźnik do struktury TRACKMOUSEEVENT:

typedef struct tagTRACKMOUSEEVENT {
    DWORD cbSize;
    DWORD dwFlags;
    HWND  hwndTrack;
    DWORD dwHoverTime;
} TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;

cbSize - chyba wiadomo;
dwFlags:

#define TME_HOVER       0x00000001        - czuwanie czy mysz nie wchodzi na obszar okna
#define TME_LEAVE       0x00000002      - czuwanie czy mysz nie opuściła
#define TME_QUERY       0x40000000      - nie pamiętam
#define TME_CANCEL      0x80000000      - anulowanie czuwania

hwndTrack - okno
dwHoverTime - czas czuwania jeśli TME_HOVER jest ustawiony

I po czwarte:
Już pewnie wpisujecie kod i do dzieła (tak też zrobiłem jak odnalazłem tą funkcję), a VC na to, że:
undefinded symbol: TrackMouseEvent()
I wogóle nie zna anie TME_LEAVE, anie WM_MOUSELEAVE czy struktury TRACKMOUSEEVENT
Znajdują się one w jakimś nagłówku stansardowym, ale jakieś dafinicje nie pozwalają im się kompilować???
Dlatego podałem w poście wszystkie ważne definicje i stałe

#ifndef WM_MOUSELEAVE
#define WM_MOUSELEAVE                   0x02A3
#endif

, które musicie dołączyć.
Build.........
No i errorów mniej, bo undefinded przechodzi w unreferenced TrackMouseEvent: znalezioną przez siebie definicję funkcji

 WINUSERAPI BOOL
WINAPI
TrackMouseEvent(
    LPTRACKMOUSEEVENT lpEventTrack);

musiałem poprzedzić

extern "C"

i wszystko gra.

Link do komentarza
Udostępnij na innych stronach

No rzeczywiscie chyba tak mozna rozwiazac. Odrazu powiem, ze sposob nie zadziala w windows 95. Moze i leciwy system, ale i nie ma obslugi tej funkcji w api.

p.s. a czy przypadkiem ten czas nie przeznaczyles na rozwiazanie problemow. Wydaje mi sie, ze jak pisales pierwsza podpowiedz w tym poscie to miales inna roznice post. :) 

Link do komentarza
Udostępnij na innych stronach

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gość
Odpowiedz...

×   Wkleiłeś zawartość bez formatowania.   Usuń formatowanie

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Utwórz nowe...