Tabstops
-
Hi!
Ich habe mir eine Gui mit Hilfe der MFC erstellt. Hat soweit auch
ohne große Probleme funktioniert, nur die Tabstops machen eine Ausnahme.Meine Vorgehensweise:
Ich stelle ein Panel bereit, dass Buttons beinhaltet. Dieses Panel soll dann vom Benutzer
selbst auf der Gui platziert werden. Dies kann auch an mehreren Seiten der Fall sein.Also habe ich ein CWnd genommen und Platziere darauf die Buttons, dass ich dann nur noch
das CWnd instanzieren und auf der Gui anbringen muss.Ich habe eine neue Klasse erstellt, die von CWnd abgeleitet ist, die Initalisierung läuft wie folgt ab:
Create(NULL, NULL, WS_CHILD|WS_VISIBLE|WS_BORDER, rcWnd, pParentWnd, IDC_WND1);
m_btn01.Create(NULL, WS_CHILD|WS_TABSTOP|WS_VISIBLE|BS_BITMAP,rcBtn01, this, IDC_BUTTON1);
m_btn02.Create(NULL, WS_CHILD|WS_TABSTOP|WS_VISIBLE|BS_BITMAP,rcBtn02, this, IDC_BUTTON2);
m_btn03.Create(NULL, WS_CHILD|WS_TABSTOP|WS_VISIBLE|BS_BITMAP,rcBtn03, this, IDC_BUTTON3);pParentWnd ist das Fenster auf dem dieses Fenster platziert werden soll.
Wenn ich jetzt auf der GUI Tab drücke, dann reagiert die GUI auf alle Controls, nur nicht auf die
aus dem Dynamischen-Configuraiton-Panel. Woran kann das liegen?So, dann bedanke ich mich schon einmal fürs lesen!
Grüße chriss_2oo4
-
Hi,
als ich mir vorher den Beitrag durchgelesen habe, ist mir aufgefallen dass ich nach einem ähnlichen Schema dynamisch Buttons erstelle und bishier noch garnie probiert habe, ob die Tab-Stops funktionieren.
Habs gerade mal ausprobiert und bei mir funktioniert das Ganze auch nicht :(.
Ich erzeuge dynamisch Komponenten, also nicht nur Buttons sondern auch ComboBoxen und Labels und füge diese zu einem Tab-Control hinzu.Falls jemand eine Lösung hätte, oder Du eine gefunden hast, wäre das super!
Lg
Kerberos
-
Es fehlt der Stil WS_TABSTOP!
Wichtig. das ganze kann nur in Views funktionieren, die
1. VOn CFormView abgeleitet sind
2. Eingebette Fenster abgelietet vom Typ CDialog besitzen
3. Wenn in dem sonstogen CView PreTranslateMessage implementiert wird und dort IsDialogMessage verwendet wird.
-
Hi Martin,
Also das WS_TABSTOP hab ich auch so, wie es vom Thread-Starter im Create-Aufruf gepostet wurde.
1. VOn CFormView abgeleitet sind
Heisst das, wenn ich meine Buttons auf ein CTabCtrl platziere, dass das Ganze garnicht funktionieren kann?
2. Eingebette Fenster abgelietet vom Typ CDialog besitzen
Die Controls packe ich auf ein CDialog-Panel, dass ich wiederum auf dem CTabCtrl platziere.
3. Wenn in dem sonstogen CView PreTranslateMessage implementiert wird und dort IsDialogMessage verwendet wird.
Etwa So?
BOOL CMyDlg::PreTranslateMessage( msg ) { if( IsDialogMessage( msg ) ) return TRUE; else return CWnd::PreTranslateMessage( msg ); }Lg und Danke
Kerberos
-
Hi,
ich hab gestern noch Zuhause den ganzen Abend versucht das mit den Tab-Stops hinzukriegen.
Ich bin jetzt zwar einen Schritt weiter als Vorher, aber es funktioniret nicht so wie es soll.Die Methode PreTranslateMessage wird aufgerufen wenn eine Nachricht gesendet wurde, aber noch bevor diese von der Anwendung verarbeitet wird.
Mit der Funktion IsDialogMessage( MSG ) prüfe ich, ob es sich um eine "Dialog-Nachricht" handelt (Tab, Pfeil-Tasten, ...) ist dass der Fall kehre ich aus der Funktion zurück.Soviel zur Theorie, nun nochmal zu meiner Vorgehensweise. Ich verwende eine SDI, das die Daten auf einer FormView anzeigt.
Auf das FormView habe ich mit dem GUI-Editor ein CTabCtrl gepackt.
Dann habe ich eine Klasse (wie von dir beschrieben von CDialog abgeleitet) die als Panel für die Elemente dienen soll.
Die einzelnen Komponenten werden von der Klasse selbst verwaltet. Jedes Control, das mit Tab angesprochen werden soll, besitzt eine eigene ID und wurde beim Erstellen mit dem Style WS_TABSTOP gekennzeichnet.Die Anwendung erstellt nun dynamisch Objekte dieser Klasse und sie werden zum TabCtrl (Parent) hinzugefügt.
Nun habe ich das PreTranslate zuerst, wie von dir beschrieben, das PreTranslate der CFormView Klasse überschrieben. Das Hatte aber keinen Effekt, weil das CFormView ja schon auf die Tab-Taste reagiert hat.
Ich konnte die einzelnen Reiter damit durchschalten.Dann bin ich so vorgegangen, wie ich eigentlich dachte dass es funktionieren sollte, ich habe die Message in der PreTranslate in CTabCtrl-Klasse abgefangen.
Dann habe ich folgendes Resultat erhalten: Die Tab-Taste funktioniert garnicht mehr (auch nicht mehr auf die Tab-Reiter) aber die Pfeiltasten reagieren die Panels. Jedoch wird immer nur ein Button angesprungen
die restlichen Controls reagieren nicht auf die Pfeil-Tasten.Dann habe ich noch versucht, die Message in der PreTranslate in CDialog-Klasse (Panel) abzufangen. Dann reagiert die Tab-Taste nur auf das Letze Panel auf dem CTabCtrl und schaltet alle Controls auf dem Panel durch.
Klicke ich mit der Maus auf ein anderes Panel, dann reagiert die Tab-Taste auf dieses Panel. Die Tab-Reiter werden in diesem Fall überhauptnicht angesprungen.Das war jetzt viel Text, also nochmals danke fürs lesen, aber ich weiss nicht wie ich an dieser Stelle weiterkommen soll?
Lg Kerberos
-
Dein Problem ist, dass offensichtlich Deine Unterdialoge Kinder des CTabCtrls sind. Das geht so niht. Dann müsste WS_EX_CONTROLPARENT für das Tab Control gesetzt sein.
Wenn aber auch Dein Tab Control den Focus erhalten soll, beißt sich hier alles in den Schwanz.Schau Dir mal an wie Windows das mit CPropertySheet macht:
http://msdn.microsoft.com/en-us/library/139z22ye(VS.80).aspxWarum baust Du das selber und greifst nicht auf entsprechenden Code zurück:
http://www.codeproject.com/KB/docview/cpropertyview.aspx
-
BTW: Wurde hier
http://groups.google.de/group/microsoft.public.de.vc/browse_frm/thread/7e90a2dee7196cde#
kürzlich auch schon diskutiert!
-
Hi Martin,
vielen Dank für Deine Hilfe!
Ich hab mir gleich einmal die Demos von CodeProject heruntergelanden und angeschaut. Leider ist es da auch so, dass ich mit der Tab-Taste, die Tab-Reiter nicht erreichen kann.
Mir persönlich würde es auch schon reichen wenn ich in meiner aktuellen Anwendung alle Controls auf einem Tab durchlaufen könnte. Hast du evtl. einen Tipp?
Das PropertySheet hab ich aus folgendem Grund nicht eingesetzt.
Ich füge Komponenten dynamisch zu einer Seite hinzu, dann sind diese Komponenten Child einer bestimmten Seite.
Nun ist es möglich, dass ich einige Komponenten nicht mehr benötige und sich dadurch die Anzahl der Seiten reduziert. Benötigte Komponenten befinden sich aber noch auf der Seite die entfernt werden soll.
Ich kann zwar die Komponenten verschieben und das Parent-Fenster aus der Sicht des Controls ändern, aber die zu löschende Seite bekommt davon nichts mit und geht immer noch davon aus, dass die verschobene Komponente ein Child-Objekt wäre, lösche ich diese Seite wird auch die Komponente gelöscht.
Das ist alles nicht so einfach, vorallem wenn man programmieren mit C# gewöhnt ist

-
kerberos schrieb:
Ich hab mir gleich einmal die Demos von CodeProject heruntergelanden und angeschaut. Leider ist es da auch so, dass ich mit der Tab-Taste, die Tab-Reiter nicht erreichen kann.
Mir persönlich würde es auch schon reichen wenn ich in meiner aktuellen Anwendung alle Controls auf einem Tab durchlaufen könnte. Hast du evtl. einen Tipp?
Das ist der Nromalfall, wenn ein CDialog bzw. CFormView verwendet wird.
Da muss man nichts machen! Dann wäre der CodeProject Code doch Deine Lösung!
Das PropertySheet hab ich aus folgendem Grund nicht eingesetzt.
Ich füge Komponenten dynamisch zu einer Seite hinzu, dann sind diese Komponenten Child einer bestimmten Seite.Nun ist es möglich, dass ich einige Komponenten nicht mehr benötige und sich dadurch die Anzahl der Seiten reduziert. Benötigte Komponenten befinden sich aber noch auf der Seite die entfernt werden soll.
Und wo ist das Problem?
Ich kann zwar die Komponenten verschieben und das Parent-Fenster aus der Sicht des Controls ändern, aber die zu löschende Seite bekommt davon nichts mit und geht immer noch davon aus, dass die verschobene Komponente ein Child-Objekt wäre, lösche ich diese Seite wird auch die Komponente gelöscht.
Verstehe ich nicht. Wnen man eine Seite löscht, dann löscht man diese und. Dann ist diese und alle Subfenster weg.
Ebenso kann man neue hinzufügen. Ich verstehe aktuell gar nichts von Deinem Design.Schau Dir bittemal die grundsätzliche Diskussion an, die ich Dir gepostet habe. Vielleiucht verstehst Du dann die Grundprinzipien, wie IsDialogMessage WS_EX_CONTROLPARENT funktioniert.
Wichtig ist auch, dass IsDialogMessage nicht von dem entsprechenden Child Dialog ausgeführt wird, sondern von dem außeren View, wenn WM_EX_CONTROLPARENT im Spiel ist.
-
Hi,
also mir ist heute Nachmittag eine "leichtere" Lösung eingefallen. Ich poste mal meinen Lösungsvorschlag für alle die ein ähnliches Problem haben (chriss_2oo4).
Ich packe einfach alle GUI-Element inkl. dem CTabCtrl als Geschwister auf der CFormView. Verwalten kann ich das ganze ja immer noch über eine eigene Klasse.
Funktioniert tadellos.
Lg Kerberos
-
Exakt. So macht es auch CPropertySheet!
So habe ich es auch in dem Thread
http://groups.google.de/group/microsoft.public.de.vc/browse_frm/thread/7e90a2dee7196cde#
angeraten.
-
Hi,
nochmals Danke, Maritn!
ein Problem gelöst, schon kommt das nächste Problem :).
Jetzt funktioniert das Durchlaufen der Controls mit der Tab-Taste zwar einwandfrei, nur wird nicht automatisch weitergescrollt wenn ein Control außerhalb des sichtbaren Bereichs liegt.
Soetwas wie ein Flag AutoScroll habe ich nicht gefunden, gibt es wahrscheinlich auch nicht, oder?Nun ist die Frage, wie kann ich das implementieren?
Meine einzige Lösung, die mir einfällt ist:
in der OnCommand die Nachricht von jedem Control abfangen und Prüfen ob ein Control den Fokus bekommen hat.
Ist dies der Fall, muss man die Koordinaten des controls verwenden um die Scroll-Position anzupassen.Ich finde diese Methode etwas umständlich, desshalb frag ich nochmal nach, vllt. gibt es ja schon was fertiges in den MFC, oder einen wesentlich besseren Lösungsweg?
Lg Kerberos
-
Nein! So wie Du das beschriebst ist es einekorrekte Lösung.
Ich verwende oft eine Helper Klasse, die einen Subclass macht und auf WM_SETFOCUS lauert.Aber es geht auch mit einem Timer. Alle 1/20 Sekunde schaut man nach wer den Focus hat. Ist das Control das den Focus hat außerhalb des sichtbaren Scrollbereiches, dann rollt man es in den sichtbaren Bereich...
-
Hi,
hat vllt. jemand einen Beispiel-Code? Ich bekomm das einfach nicht hin. Ich kann ja mal meine bisherigen Überlegungen posten:
pWnd = GetDlgItem(iId); pWnd -> GetWindowRect(rcComponent); //Es muss nach links gescrollt werden if(rcComponent.left < rcParent.left + GetScrollPos(SB_HORZ)) { int iOffset = rcComponent.left - rcParent.left; ScrollWindow(SB_HORZ, iOffset); } //Es muss nach rechts gescrollt werden else if(rcComponent.right > rcParent.right + GetScrollPos(SB_HORZ)) { int iOffset = rcComponent.right - rcParent.right; ScrollWindow(SB_HORZ, iOffset); }rcParent ist das CRect der CFormView und rcComponent ist das CRect der aktuellen Componente (z. B. Button).
Danke und Lg
Kerberos
-
Du hast doch en CScrollView! Dann nutze das doch.
ScrollWindow ist IMHO der falsche Ansatz. Zur Not (wenn kein CScrollView im Spiel ist) sende einen WM_xSCROLL/thumb Nachricht an Dich selbst und behandle das dort.Rollen musst da jetzt auch schon können. Es macht doch keinen Sinn, den Rollmechanismus überall in Deiner Klasse zu streuen.
Bei einem Scrollview ist das ganz einfach:
// pView ist ein CScrollView // Diese Werte Brauchen wir CRect rcControl, rcParent; // Erstmal das Client pView->GetClientRect(&rcParent); // Fenster Koordinaten bzgl. des Parents Wnd()->GetWindowRect(&rcControl); pView->ScreenToClient(&rcControl); // Aktuelle Position bestimmen CPoint ptScrollPosition= pView->GetScrollPosition(); // Verticales rollen? CPoint ptNewScrollPosition = ptScrollPosition; // Eine Unit Platz lasen CSize sOffset = CSize(irgendwasx,irgendwasy); // Vertikales rollen if (rcControl.left < rcParent.left) // Verschieben nach links (Distanz ist Negativ) ptNewScrollPosition.x += rcControl.left-rcParent.left-sOffset.cx; else if (rcControl.right > rcParent.right) // Verschieben nach rechts (Distanz ist positv) ptNewScrollPosition.x += rcControl.right-rcParent.right+sOffset.cx; // Horizontales rollen if (rcControl.top < rcParent.top) // Verschieben nach oben (Distanz ist Negativ) ptNewScrollPosition.y += rcControl.top-rcParent.top-sOffset.cy; else if (rcControl.bottom > rcParent.bottom) // Verschieben nach unten (Distanz ist positv) ptNewScrollPosition.y += rcControl.bottom-rcParent.bottom+sOffset.cy; /* Nun bliebt die Frage ob durch diesen Scroll Vorgang etwa * die linke obere Ecke aus dem Fenster gerät */ rcControl.OffsetRect(ptScrollPosition-ptNewScrollPosition); if (rcControl.left<0) ptNewScrollPosition.x += rcControl.left; if (rcControl.top<0) ptNewScrollPosition.y += rcControl.top; // Scroll nur wenn notwendig if (ptScrollPosition!=ptNewScrollPosition) pView->ScrollToPosition(ptNewScrollPosition);
-
Hi Martin,
vieeelen Daaaank, da wäre ich wohl noch zwei Tage dran gesessen...
Wie ich mir dachte, ein Problem gelöst schon taucht das nächste auf
Meine Anwendung springt nur in die OnCommand wenn ich durch das Drücken der Tab-Taste auf eine Combo-Box komme, erreiche ich einen Button, komme ich erst garnicht in die OnCommand. Muss ich jetzt wieder irgendwelche "Spezialänderungen" an meiner Anwendung vornehmen oder muss ich das Ganze in einen Timer verlagern, sobald ich Buttons einsetze?
-
Buttons haben keine Benachrichtigung für Focus bekommen/verlieren.
Also Subclass Hook setzen und WM_SETFOCUS abfangen oder Timer nutzen. (Sagte ich das nicht schon...)
-
fehlpost
-
Hi,
Ich hab den Thread gestartet, und mich auch inzwischen angemeldet. Ich hab das Ganze jetzt versucht zu implementieren, aber bin auf ein Problem gestoßen
In einer Klasse die von CFormView abgeleitet ist gibt es die Methode ScrollToPosition(...) und die funktioniert auch ganz prima, aber in meiner Klasse, die von CDialog abgeleitet ist gibt es das leider nicht.
Da gibt es nur folgende zwei Methoden
SetScrollPos(...);
ScrollWindow(...);Ich hab OnHScroll(...) überschrieben und ich komm mit dem Zusammenspiel beider Methoden (SetScrollPos / ScrollWindow) und (OnHScroll / Timer) nicht klar.
void CExpertModeDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int iDelta; switch (nSBCode) { case SB_LINERIGHT: if (m_iHScrollPos >= m_iHScrollWidth) { return; } iDelta = 5; break; case SB_LINELEFT: if (m_iHScrollPos <= 0) { return; } iDelta = -5; break; case SB_PAGERIGHT: if (m_iHScrollPos >= m_iHScrollWidth) { return; } iDelta = 5; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: iDelta = (int)nPos - m_iHScrollPos; break; case SB_PAGELEFT: if (m_iHScrollPos <= 0) { return; } iDelta = -5; break; default: return; } m_iHScrollPos += iDelta; SetScrollPos(SB_HORZ, m_iHScrollPos); ScrollWindow(-iDelta, 0); CDialog::OnHScroll(nSBCode, nPos, pScrollBar); }m_iHScrollPos ist die aktuelle Position des "Scrollers"
m_iHScrollWidth ergibt sich aus der Breite des anzuzeigenden Bereichs (tatsächliche Breite) minus der aktuellen Breite des Fensters.
Ich bin jetzt einfach wie folgt im Timer vorgegangen, aber dann wird immer mein ganzes Fenster verschoben und ich kann nicht zurückscrollen.
Implementierung im Timer
CWnd* pWnd = GetFocus(); CRect rcControl, rcParent; int iDelta = 0; GetClientRect(&rcParent); pWnd -> GetWindowRect(rcControl); ScreenToClient(&rcControl); // Aktuelle Position bestimmen CPoint ptScrollPosition = GetScrollPos(SB_HORZ); CPoint ptNewScrollPosition = ptScrollPosition; // Eine Unit Platz lasen CSize sOffset = CSize(20,20); if (rcControl.left < rcParent.left) { iDelta = rcControl.left - rcParent.left - sOffset.cx; ptNewScrollPosition.x += iDelta; } else if (rcControl.right > rcParent.right) { iDelta = rcControl.right - rcParent.right + sOffset.cx; ptNewScrollPosition.x += iDelta; } rcControl.OffsetRect(ptScrollPosition-ptNewScrollPosition); if (rcControl.left < 0) { ptNewScrollPosition.x += rcControl.left; } if (rcControl.top < 0) { ptNewScrollPosition.y += rcControl.top; } if (ptScrollPosition!=ptNewScrollPosition) { m_iHScrollPos += iDelta; SetScrollPos(SB_HORZ, m_iHScrollPos); ScrollWindow(-iDelta, ptNewScrollPosition.y); }Grüße
Chris
-
Und warum benutzt Du einen Dialog?
Stilistisch halte ich von rollbaren Dialogen gar nichts. Nimm ein CPropetySheet mit mehreren Seiten um die Dialoge vernünftig aufzuteilen.Ansonsten:
http://www.codeproject.com/KB/dialog/scrolling_support.aspx