Win32 Ownerdraw Subclassed Button kein Tooltip?



  • Hallo,
    ich habe eine Klasse geschriben, in der ich einen Ownerdraw Button habe, der unter anderem auch subclassed ist... Jetzt wollte ich für diesen Button auch noch ein Tooltip erstellen. Auserhalb der klasse bei einem normalen Window funktioniert dieser Code für das Tooltip erstellen einwandfrei, keine Ahnung warum es hier nicht funzt...

    Hier ist mein code:

    LRESULT CALLBACK CHyperlink::WndProcLinkButton(HWND hWndLinkButton, UINT msg, WPARAM wParam, LPARAM lParam) // Für Subclassed Button
    {
    switch (msg)
    	{
    		case WM_MOUSEMOVE:
    			{
    				HCURSOR hCursor = LoadCursor(NULL, IDC_HAND);
    				if (NULL == hCursor)
    				{
    					hCursor = LoadCursor(NULL, IDC_ARROW);
    				}
    				SetCursor(hCursor);
    				TRACKMOUSEEVENT tme = {0};
    				tme.cbSize = sizeof(tme);
    				tme.dwFlags = TME_LEAVE;
    				tme.hwndTrack = hWndLinkButton;
    				_TrackMouseEvent(&tme);	
    				break;
    			}
    		case WM_MOUSELEAVE:
    			{
    				PostMessage(GetParent(hWndLinkButton),WM_MOUSELEAVE,0,0);
    				return DefWindowProc( GetParent(GetParent(hWndLinkButton)), WM_SETCURSOR, 0, 0 );
    				break;
    			}
    		}
    	// Any messages we don't process must be passed onto the original window function
    	return CallWindowProc((WNDPROC)prev_procLinkButton, hWndLinkButton, msg, wParam, lParam);
    }
    
    LRESULT CALLBACK CHyperlink::WinMsgHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) // WindowProc
    {
    	switch( msg )
    	{
    		case WM_SETFONT:
    			{
    				SendMessage(GetDlgItem(hwnd,1), WM_SETFONT, wParam, lParam);
    				break;
    			}
    
    		case WM_GETFONT:
    			{
    				return SendMessage(GetDlgItem(hwnd,1), WM_GETFONT, 0, 0);
    			}
    
    		case WM_SETTEXT:
    			{
    				SetText(GetDlgItem(hwnd,1),(LPCWSTR)lParam);
    				break;
    			}
    
    		case WM_CREATE:
    			{
    				wchar_t WindowText[500];
    				GetWindowText(hwnd, WindowText, 500);
    				RECT rect;
    				GetClientRect(hwnd,&rect);
    				CreateWindow(L"Button", WindowText, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 0, 0, rect.right, rect.bottom, hwnd, (HMENU)1, NULL, (LPVOID)lParam);
    				SendMessage(GetDlgItem(hwnd,1), WM_SETFONT, (WPARAM) HyperlinkFont, TRUE);
    				UpdateWindow(GetDlgItem(hwnd,1));
    				WNDCLASSEX wcx;
    				wcx.cbSize = sizeof(WNDCLASSEX);
    				wcx.lpfnWndProc = WndProcLinkButton;
    				prev_procLinkButton = ::SetWindowLongPtr(GetDlgItem(hwnd,1), GWLP_WNDPROC, (LONG)wcx.lpfnWndProc);
                    // Tooltip erstellen
    				CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP|TTS_ALWAYSTIP, 0,0,0,0, hwnd, (HMENU)3, NULL, (LPVOID)lParam);
    				SendMessage(GetDlgItem(hwnd,3), TTM_ACTIVATE, TRUE, 0);
    				TOOLINFO toolinfo; // Tool Tip Info structure
    				memset(&toolinfo, 0, sizeof(TOOLINFO));
    				toolinfo.cbSize = sizeof(TOOLINFO);
    				toolinfo.hwnd = hwnd;
    				toolinfo.uFlags = TTF_SUBCLASS | TTF_IDISHWND;
    				toolinfo.hinst = NULL;
    				toolinfo.uId = (UINT_PTR)GetDlgItem(hwnd,1);
    				toolinfo.lpszText = const_cast<LPWSTR>(Url.c_str()); // Text you wish displayed when the mouse is over the control
    				SendMessage (GetDlgItem(hwnd,3), TTM_ADDTOOL, 0, (LPARAM)&toolinfo);
                    // End Tooltip
    				break;
    			}
    
    		case WM_COMMAND:
    			{
    				bVisited = TRUE;
    				ShellExecute(NULL, L"Open", Url.c_str(), NULL, NULL, 1);
    				break;
    			}
    
    		case WM_MOUSELEAVE:
    			{
    				HFONT CurFont = (HFONT)SendMessage(GetDlgItem(hwnd,1), WM_GETFONT, 0, 0);
    				LOGFONT lf = {0};
    				GetObject(CurFont, sizeof(lf), &lf);
    				lf.lfUnderline = (BYTE) FALSE;
    				HFONT NewFont = CreateFontIndirect(&lf);
    				SendMessage(GetDlgItem(hwnd,1),WM_SETFONT, (WPARAM) NewFont, TRUE);
    
    				if(bMouseOverLinkButton)
    				{
    					bMouseOverLinkButton = FALSE;
    					if(bVisited)cActual = cVisited;
    					else		cActual = cNormal;
    					InvalidateRect(GetDlgItem(hwnd,1), 0, FALSE);
    				}
    				break;
    			}
    
    		case WM_SETCURSOR:
    			{
    				HCURSOR hCursor = LoadCursor(NULL, IDC_HAND);
    				if (NULL == hCursor)
    				{
    					hCursor = LoadCursor(NULL, IDC_ARROW);
    				}
    				SetCursor(hCursor);
    
    				HFONT CurFont = (HFONT)SendMessage(GetDlgItem(hwnd,1), WM_GETFONT, 0, 0);
    				LOGFONT lf = {0};
    				GetObject(CurFont, sizeof(lf), &lf);
    				lf.lfUnderline = (BYTE) TRUE;
    				HFONT NewFont = CreateFontIndirect(&lf);
    				SendMessage(GetDlgItem(hwnd,1),WM_SETFONT, (WPARAM) NewFont, TRUE);
    
    				if(!bMouseOverLinkButton)
    				{
    					bMouseOverLinkButton = TRUE;
    					cActual = cMouseOver;
    					InvalidateRect(GetDlgItem(hwnd,1), NULL, FALSE);
    				}
    				break;
    			}
    
    		case WM_DRAWITEM:
    			{
    				DRAWITEMSTRUCT *dis = (LPDRAWITEMSTRUCT)lParam;
    				HGDIOBJ OldObj;
    				wchar_t text[500];
    				HBRUSH aBrush;
    				RECT rt;    
    
    				// Damit man später nicht immer "dis->rcItem" schreiben muss:
    				rt.left=dis->rcItem.left;
    				rt.top=dis->rcItem.top;
    				rt.right=dis->rcItem.right;
    				rt.bottom=dis->rcItem.bottom;
    
    				// Bei itemAction ist das Style ODA_DRAWENTIRE enthalten, also
    				// muss alles gezeichnet werden:
    				if (dis->itemAction & ODA_DRAWENTIRE)    
    				{
    					// Hintergrund zeichnen:
    						aBrush=GetSysColorBrush(COLOR_BTNFACE);
    						OldObj=SelectObject(dis->hDC,aBrush);
    							PatBlt(dis->hDC,1,1,(rt.right-2),(rt.bottom-2),PATCOPY);
    						DeleteObject(SelectObject(dis->hDC,OldObj));
    
    					// Text holen:
    						GetWindowText(dis->hwndItem,text,500);
    
    					// Text zeichnen:
    						// Standard-Schriftart holen:
    							OldObj=SelectObject(dis->hDC,GetStockObject((int)CreateFont(20,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Microsoft Sans Serif"))));
    						// Hintergrund des Textes auf Transparent schalten:
    							SetBkMode(dis->hDC,TRANSPARENT);
    						// Textfarbe setzen:
    							SetTextColor(dis->hDC,cActual);
    						// Und nun endlich zeichnen:
    							DrawText(dis->hDC,text,wcslen(text),&rt,DT_LEFT);  // Caption zeichnen
    						// Schriftart wieder freigeben und altes Objekt wieder selektieren:
    							DeleteObject(SelectObject(dis->hDC,OldObj));
    				}
    				return true;
    			}
    
    		case WM_CLOSE:
    			{
    				HWND hWndParent = GetParent(hwnd);
    				DestroyWindow(hwnd);
    				SetActiveWindow(hWndParent);
    				break;
    			}
    
    		default:
    			return DefWindowProc( hwnd, msg, wParam, lParam );
    	}
    }
    

    Vielen Dank für eure Hilfe

    Johannes


  • Mod

    1. Wir kommst Du darauf einfach eine Windows Nachrich an ein Parent weitersenden zu können?
    2. Du erzeugst ja permanent neue Fonts. Wer entsorgt die bitte?



  • Hallo,
    Danke für die Antwort, ich bin 13 Jahre alt und habe mir in den Sommerferien das C++ beigebracht und das sind Teile meiner ersten Ergebnisse. Das mit den Fonts glaube ich verstanden zu haben, mit DeleteObject() oder? Aber das mit dem Parent Window verstehe ich nicht so, Ich wollte das WM_SETFONT zu dem Partent senden damit er die Cursors wieder auf Normal setzt. Es funktioniert ja auch, obwohl ich einsehe, dass ein Child nicht's zu einem Parent senden sollte. Ich wüsste nicht wie ich das anders machen sollte, aber bin über jede Hilfe dankbar!

    Grüsse
    Johannes


  • Mod

    1. Du sendest die Windowsnachricht WM_MOUSELEAVE an das Parent.
    Man sollte niemals solche "internen" Windows-Nachrichten an andere Fenster umlenken. Wenn Du Deinem Parent was mittelen willst nimm eine WM_APP oder WM_USER Nachricht.
    2. Ja! Du musst einen Font mit DeleteObject löschen. Ich würde Dir aber nicht raten diese Objekte herzustellen und sofort wieder zu löschen.



  • Vielen Dank für die Antwort,
    wie würdest du den das mit den Fonts machen wenn nicht Herstellen und dann wieder löschen?

    Vielen Dank für deine Hilfe!
    Johannes



  • Recycling. 😉



  • Wie soll ich das jetzt bitte interpretieren??? Soll ich globale Variablen erstellen?



  • Und noch eine kurze Frage, weiss den jetzt irgendjemand warum das mit dem Tooltip nicht fuktioniert?

    Nochmals Danke
    Johannes


  • Mod

    Evtl. durch Deine falsche Behandlung von WM_MOUSELEAVE!



  • Hallo Johannes,

    in der WinMsgHandler-Prozedur gibst Du nur in den wenigsten Fällen einen Wert zurück.
    Somit verbleibt in R/EAX dann meist der Rückgabewert des letzten Funktionsaufrufs, wenn direkt danach ein break kommt.

    Ich würde tippen, dass dort der Hund begraben ist.
    Sieh in der Doku nach, welchen Rückgabewert die jeweiligen Nachrichten verlangen.

    Grüße
    Greenhorn

    p.s. warum ist dieser Thread so bescheiden formatiert ?
    p.p.s. Deinen Geburtstag haben Deine Eltern geschickt geplant, gell 😉 😃



  • Hallo,
    Vielen Dank für eure Hilfe! Leider funktioniert es noch immer nicht... Jetzt geben alle Messages einen Wert zurück, habe in der MSDN nachgeschaut. Und das WM_MOUSELEAVE habe ich jetzt durch WM_USER ausgetauscht.

    Also jetzt sied das ganze so aus:

    CHyperlink::WndProcLinkButton

    case WM_MOUSELEAVE:
    			{
    				PostMessage(GetParent(hWndLinkButton),WM_USER,0,0);
    				DefWindowProc( GetParent(GetParent(hWndLinkButton)), WM_SETCURSOR, 0, 0 );
    				return 0;
    				break;
    			}
    

    CHyperlink::WinMsgHandler

    case WM_USER:
    			{
    				HFONT CurFont = (HFONT)SendMessage(GetDlgItem(hwnd,1), WM_GETFONT, 0, 0);
    				LOGFONT lf = {0};
    				GetObject(CurFont, sizeof(lf), &lf);
    				lf.lfUnderline = (BYTE) FALSE;
    				HFONT NewFont = CreateFontIndirect(&lf);
    				SendMessage(GetDlgItem(hwnd,1),WM_SETFONT, (WPARAM) NewFont, TRUE);
    
    				if(bMouseOverLinkButton)
    				{
    					bMouseOverLinkButton = FALSE;
    					if(bVisited)cActual = cVisited;
    					else		cActual = cNormal;
    					InvalidateRect(GetDlgItem(hwnd,1), 0, FALSE);
    				}
    				return 0;
    				break;
    			}
    

    Und wenn ich die Fonts immer mit DeleteObject lösche, dan ändert er mir die Font(Ich glaube auf die WIndows-DefaultFont), und tut sie nicht nur einfach "Underlinen", habe ich da dann ewentuell auch irgendwo noch einen Fehler?

    Und mir ist gerade aufgefallen das unter Windows XP meine Icons einfach verschwunden sind, und da jetzt der default AppIcon überall ist, unter Windows 7 zeigt er sie ohne Probleme an...

    Vielen Dank
    Johannes



  • Hallo,
    das mit den Icons auf XP habe ich jezt gelöhst, hatte versehendlich ein 256x256 icon benutzt 😃 .
    Aber für die anderen beiden Probleme habe ich noch keine Lösung...


  • Mod

    Diu kannst Fonts nicht löschen, wenn Du sie noch irgendwo benutzt. Genaugenommen muss das Parent den Font erzeugen. Für seine Child sezen und das Handler erst entsorgen wenn alle Kinder gestorben sind.
    Also brauchst Du den Font als Variable in dem Fenster zu seiner Lebenszeit.



  • Hallo,
    danke für die Antwort, wenn ich das richtig verstanden habe dan soll ich einfach in meiner Klasse eine Variable definieren mit dem Font, richtig? Und die dan immer benutzen? Wenn ja, das hatte ich schon mal ausprobiert habe es aber danach wieder rückgängig gemacht weil da hatte ich auch Probleme mit den Fonts: er hat mir auch die Font einfach geändert, ich glaube zwar nicht in die Default, aber einfach eine andere!

    Um genauer zu beschreiben was ich gemacht habe:
    - Ich habe eine Variable mit der Font die ich am Anfang initialisiere mit so zu sagen "meiner Default Font".
    - Dan in WM_CREATE weise ich diesen Font meinem Button zu(Bis hier klappts).
    - Aber danach sobald ich auf den Button komme mit der Maus, also beim Underlinen, ändert er mir die Font! Und ab dem Zeitpunkt bleibt es die Andere (Underlined oder nicht).

    Vielen Dank
    Johannes


Anmelden zum Antworten