Probleme mit Subclassing



  • Hallo,
    ich befasse mich das erste Mal mit Subclassing und habe seit 4 Stunden versucht meine Unkentniss auszumerzen. Theorethisch verstehe ich das Prinzip auch, aber praktisch geht da irgendwie gar nichts.

    Ich möchte in meinem kleinen Bootstrapper einfach nur zwischen einer ComboBox und zwei Buttons per TAB-navigieren. Dürfte ja eigentlich nicht so schwer sein. Das Problem ist ja nur, dass, wenn ein zB Button den Focus hat, er alle Tastatur-Keys bis auf Space verschluckt, sprich, ich kann gar nicht weiterschalten.

    Ich habe gelesen, dass dieses Problem mit Subclassing einfach zu realisieren sei. Hier meine relevanten Snippets:

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
       switch(message){
           case WM_CREATE:
    	pB2 = CreateWindow("BUTTON", "Cancel", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 265, 107, 80, 25, hWnd, NULL, ((LPCREATESTRUCT) lParam) -> hInstance,  NULL);
    	pB1 = CreateWindow("BUTTON", "Ok", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 163 ,107, 100, 25, hWnd, NULL, ((LPCREATESTRUCT) lParam) -> hInstance,  NULL);
    	PrevWndProcEdit = SetWindowLongPtr (pB1, GWLP_WNDPROC, (LONG_PTR) ButtonProc);
    	break;
    
            case WM_KEYDOWN:
    	    switch (wParam){
    		case VK_TAB:
    		MessageBox(NULL, "Tab!", "KeyEvent!", MB_ICONEXCLAMATION | MB_OK);
    		//GetNextDlgTabItem(hWnd,pB1,0);
    		break;
                    }
    	break;
            }
    }
    

    Alle Variablen habe ich global und static gesetzt. Für testzwecke einfacher.
    Jetzt folgt meine ButtonProc:

    LRESULT CALLBACK ButtonProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
    	switch(message){
    
    	     case WM_LBUTTONDOWN:
    			MessageBox(NULL, "Buttonfocus und Mausdown!", "Event!", MB_ICONEXCLAMATION | MB_OK);
    			break;
    		}
    	return CallWindowProc ((WNDPROC) PrevWndProcEdit, hWnd, message, wParam, lParam);
    }
    

    Das Ergebnis sieht so aus, dass der Tab solange akzeptiert wird, solange pB1 nicht den Focus hat. Wen der Button den Focus hat, wird der Tabdruck nicht weitergeleitet. Ich dachte aber, das würde durch CallWindowProc() getan werden?

    Wie gesagt, rein theoretisch ist das Handling ganz logisch, aber so richtig klappt es halt nicht. Kann mir jemand helfen?



  • AFAIK behandelt die default-Button Proc eben diese Tasten selber und reicht sie nicht weiter... Du musst sie schon in Deiner Subclassing selber abfangen, bevor Du es der default WndProc gibst...



  • Okay, das stimmt, aber welche Messages werden den weitergereicht? Ich dachte, man kann die Messages abhandeln, die man möchte und alles weiteres wird durch CallWndProc weitergegeben, so wie es halt für eine MessageChaine üblich sein sollte.

    Heißt das jetzt, das ich in ButtonProc alle Events abfangen muss, dann das gleiche Event per SendMessage weiterreichen, um in der WndProc des Parent-Windows das Fokussing zu bearbeiten?



  • Der Button bekommt halt das WM_KEYDOWN... warum sollte er es dem Parent weiterreichen?
    Wenn Du das WM_KEYDOWN behandeln willst, dann musst Du das eben in dem Subclassing machen...



  • Gut okay, aber warum ruft man denn überhaupt CallWndProc auf, wenn das fokussierte Element sowie jedes Ereignis behandeln kann?

    Gibt es eigentlich eine Möglichkeit eine combobox vor dem Editieren durch den User zu schützen? Es ist mir nämlich nicht möglich, auf Tabs zu reagieren, wenn der Focus auf der combobox liegt???

    Mann, das ist wirklich umständlich...



  • Ich verstehe bis heute noch nicht warum Du auf TABs "reagieren" willst... eigentlich legt man die Reihenfolge mit der Z-Order fest und damit hat sichs...

    Und warum man "CallWndProc" aufruft? Wer glaubst Du reagiert denn auf die Ereignisse? Irgendein Geist im Computer?

    Eine ComboBox kannst Du vor dem Editieren schützen indem du es dieser sagts, dass sie nicht editierbar sein soll... CBS_DROPDOWNLIST

    PS: Ich würde heutzutage niemandem Raten direkt mit der WinAPI GUI Programme zu machen... das ist ineffektiv und viel zu umständlich! Nimm bitte Windows.Forms, da machst Du Dir (und den anderen) eine Freude!!

    Kannst entweder auch in C++ oder C# programmieren... die IDE gibt es sogar noch kostenlos...



  • Wo ist jetzt das Problem. Wenn du es brauchst fange halt die WM_KEYDOWN nachricht in deiner Button Proc ab und für den rest rufst du die alte Wndproc auf. So hast du es doch quasi auch stehen.



  • Warum ich auf TABs reagieren will? Naja, um den Focus weiterzuschalten. Das Programm soll über Keyboard steuerbar sein. Was das mit Z-Order ist, keine Ahnung, aber selbst dort werde ich doch erstmal auf den TAB reagieren müssen. Woher soll denn das Programm sonst wissen, dass gewechselt werden soll? Also falls ich mich da irren sollte, berichtige mich.

    Das die WndProc die Messages verarbeitet ist schon klar, aber wenn ich in der Subclass eh alle Events auffangen und separat verarbeiten muss, dann könnte man doch eigentlich dadrauf verzichten, sondern einfach in der Subclass ein neues SendMessage aufrufen. Das leuchtet mir halt nicht ganz ein, aber egal.

    Mit C# darf ich nicht arbeiten, dass das Ding ein Bootstrapper wird und man nicht von unseren Kunden erwarten kann, dass die .NET vorher installieren sollen. Ich sollte mich auf den kleinsten, auf jedem Computer verfügbaren Nenner konzentrieren und das ist nun mal die WinAPI.



  • AkonF schrieb:

    Warum ich auf TABs reagieren will? Naja, um den Focus weiterzuschalten. Das Programm soll über Keyboard steuerbar sein. Was das mit Z-Order ist, keine Ahnung, aber selbst dort werde ich doch erstmal auf den TAB reagieren müssen. Woher soll denn das Programm sonst wissen, dass gewechselt werden soll? Also falls ich mich da irren sollte, berichtige mich.

    Sobald TAB gedrückt wird, springt Windows automatisch zum nächsten Control. Da muss man nix machen... Dazu musst Du nur definieren was das nächste Control ist (VC7: Menü im Resource-Editor: "Format|Tab Order").

    AkonF schrieb:

    Das die WndProc die Messages verarbeitet ist schon klar, aber wenn ich in der Subclass eh alle Events auffangen und separat verarbeiten muss, dann könnte man doch eigentlich dadrauf verzichten, sondern einfach in der Subclass ein neues SendMessage aufrufen. Das leuchtet mir halt nicht ganz ein, aber egal.

    Du bearbeitest doch nicht ALLE Messages, oder? Oder zeichnest Du etwa den Button???

    AkonF schrieb:

    Mit C# darf ich nicht arbeiten, dass das Ding ein Bootstrapper wird und man nicht von unseren Kunden erwarten kann, dass die .NET vorher installieren sollen. Ich sollte mich auf den kleinsten, auf jedem Computer verfügbaren Nenner konzentrieren und das ist nun mal die WinAPI.

    Dann kannst Du aber zumindest die MFC nehmen...



  • IsDialogMessage



  • Dann gehst du aber davon aus, dass jeder ein Visual Studio hat, sowohl dein Hinweis mit dem Ressourcen-Editor als auch MFC. Da das aber leider kostenpflichtig ist, fällt das ganz raus. Naja, ich werd das schon irgendwie schaukeln.

    Bzgl der Fokussierung. Meine Buttons sehen so aus:

    pB2 = CreateWindow("BUTTON", "Cancel", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 265, 107, 80, 25, hWnd, NULL, ((LPCREATESTRUCT) lParam) -> hInstance,  NULL);
    pB1 = CreateWindow("BUTTON", "Ok", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 163 ,107, 100, 25, hWnd, NULL, ((LPCREATESTRUCT) lParam) -> hInstance,  NULL);
    

    Bei mir reagiert Windows nicht eigenständig auf den TAB.



  • Ok... es geht natürlich nur wenn Du auch einen Dialog hast! da der Parent (also Dialog) das ganze natürlich handhaben muss. Aber da Du eh sowas wie ein Dialog hast würde es ja genau passen.
    Und es geht natürlich auch ohne MFC/Resource-Editor...
    Also:
    dwStyle:
    WS_CAPTION|WS_POPUP|WS_VISIBLE|WS_CLIPCIBLINGS|WS_SYSMENU|DS_3DLOOK|DS_FIXEDSYS|DS_SETFONT|DS_MODALFRAME
    dwExtStyle: WS_EX_DLGMODALFRAME|WS_EX_CONTROLPARENT|WS_EX_APPWINDOW|WS_EX_WINDOWEGDE

    Und am besten nicht mit CreateWindow erzeugen sondern mit CreateDialogIndirect



  • Bzw. besser ist vielleicht noch "CreateDialogIndirect"...


Anmelden zum Antworten