CSplitterWnd & Doc/View ...
-
Hi Leutz,
ich hab mal ne Frage zu CSplitterWnd: Wenn ich eine "normale" SDI-Anwendung mit Doc/View estelle und dann in der Klasse
CMainFrm die Methode "OnCreateClient" so überschreibe, dass ich den Cleintbereich in genau 2 Panes (links einen und
rechts einen) teilen lasse, funzt alles wunderbar.
Wenn ich aber eine Anwendung OHNE Doc/View erstelle, teilt er beim gleichem Vorgehen nicht das Clientfenster, sonderndie Symbol- un Statusleiste in 2 Teile.
Daher: Weiß zufällig jemand, wo ich in einer Non-Doc/View-Anwendung splitten muss?
Bisher habe ich - dank der Klasse ST_SplitterWnd von codeproject.com - immer so gesplittet:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { m_pSplitterWnd.Create(this, RUNTIME_CLASS(CTree), RUNTIME_CLASS(CList), pContext); return TRUE; //CFrameWnd::OnCreateClient(lpcs, pContext); }
Wobei m_pSplitterWnd eine Membervariable der Klasse CMainFrame - vom Typ ST_SplitteWnd - ist. CTree und CList sind
jeweils von CView abgeleitet. Aber dieser Code führt dazu, dass - ohne Doc/View - die Symbol- und Statusleiste
gesplittet werden. Also hab ich die Membervariable auf den Typ CSplitterWnd abgeändert und e so versucht:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { m_pSplitterWnd.Create(this,1,2,CSize(700,100), pContext); m_pSplitterWnd.CreateView(0,0, RUNTIME_CLASS(CTree), CSize(175,100), pContext); m_pSplitterWnd.CreateView(0,1, RUNTIME_CLASS(CList), CSize(500,100), pContext); return TRUE; //CFrameWnd::OnCreateClient(lpcs, pContext); }
Dabei haut's ihn aber immer beim Start des Progs raus
Danke.
Happosai
-
Hi,
also so ganz ist mir nicht klar, was du vor hast. Aber Was sind CTree und CList für Klassen? Hast du da was abgeleitet? Versuche an der Stelle einfach mal CTreeView und CListView einzusetzen, und verwende deine eigenen Klassen erst, wenn das Gerüst funktioniert.
Ansonsten... Was "haut ihn raus". Du kannnst bis zu der Funktion debuggen, in der es in würfelt. Das wird eine Assertion wie IsWindow sein, oder? An welcher Stelle tritt sie auf?
Grüße, Volle.
-
Hab's (zufällig) rausgefunden, ich werd's mal als FAQ-Topic schreiben:
Um eine SDI-Anwendung ohne Doc/View-Unterstützung zu splitten, muss man Folgendes machen:
1. eine CSplitterWnd-Member-Variable zur Klasse CMainFrame hinzufügen
2. zwei Klassen von z.B. CView (oder CListView / CTreeView, je nach dem, wofür das Teilfenster später sein soll) ableiten, dabei aber unbedingt sicherstellen, dass in der Klassen-Definition das MakroDECLARE_DYNCREATE(CDieseKlasse)
enthalten ist.
3. In der Create-Methode des MainFrames ganz am Ende einfach diesen Code einfügen:if (!m_pSplitterWnd.CreateStatic(this, 1, 2)) { TRACE0("Failed to create splitter bar "); return FALSE; // failed to create } m_pSplitterWnd.CreateView(0, 0, RUNTIME_CLASS(CDeineViewKlasse1), CSize(175,100), NULL); m_pSplitterWnd.CreateView(0, 1, RUNTIME_CLASS(CDeineViewKlasse2), CSize(500,100), NULL);
Dabei ist m_pSplitterWnd die Member-Variable vom Typ CSplitterWnd.
Leitet man die Klasse 1 z.B. von CTreeView und die Klasse 2 z.B. von CListView ab, sieht das Ergebnis so aus:
http://free.pages.at/happosai/c_plusplus_de/csplitterwnd_1.jpg (ca. 37 KB)4. Nun sollte man noch zur Initialisierung der Views deren Methode OnInitialUpdate() aufrufen. Dort kann man sich mit den Methoden GetListCtrl() oder GetTreeCtrl() (ja nach Basisklasse) ein CListCtrl-Objekt holen und damit ganz normal arbeiten: (hier eine von CListView abgeleitete Klasse, in die 4 Spalten eingefügt werden sollen:
void CYourList::OnInitialUpdate() { CListView::OnInitialUpdate(); CListCtrl& lc = GetListCtrl(); lc.ModifyStyle(0, LVS_REPORT); lc.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT); lc.InsertColumn(0, _T("Spalte 1"), LVCFMT_LEFT); lc.InsertColumn(1, _T("Spalte 2"), LVCFMT_LEFT); lc.InsertColumn(2, _T("Spalte 3"), LVCFMT_LEFT); lc.InsertColumn(3, _T("Spalte 4"), LVCFMT_LEFT); lc.SetColumnWidth(0,150); lc.SetColumnWidth(1,170); lc.SetColumnWidth(2,130); lc.SetColumnWidth(3,200); }
Dann sieht die ganze Anwendung so aus:
http://free.pages.at/happosai/c_plusplus_de/csplitterwnd_2.jpg (ca. 60 KB)Das Problem: Die Initialisierung wird 2x vorgenommen. Daher legt euch am besten in der jeweiligen View-Klasse eine Member-Variable
bool m_bFirstInit;
an, die ihr im Konstruktor der Klasse mit false initialisiert. Dann schreibt ihr den ganzen Funktionsblock noch in eine if-Verzweigung und schon wird die View-Klasse nur einmal initialisiert:
void CYourList::OnInitialUpdate() { CListView::OnInitialUpdate(); CListCtrl& lc = GetListCtrl(); if(m_bFirstInit == false) return; m_bFirstInit = false; lc.ModifyStyle(0, LVS_REPORT); lc.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT); lc.InsertColumn(0, _T("Spalte 1"), LVCFMT_LEFT); lc.InsertColumn(1, _T("Spalte 2"), LVCFMT_LEFT); lc.InsertColumn(2, _T("Spalte 3"), LVCFMT_LEFT); lc.InsertColumn(3, _T("Spalte 4"), LVCFMT_LEFT); lc.SetColumnWidth(0,150); lc.SetColumnWidth(1,170); lc.SetColumnWidth(2,130); lc.SetColumnWidth(3,200); }
So, das war's schon. Alle von CView abgeleiteten Klassen dürften eine OnInitialUpdate-Methode haben, also ist der Platz zum Initialisieren immer gegeben.
Der Rest dürfte ganz normal zu handeln sein, also keine weiteren Probleme.
Wäre schön, wenn jemand diesen Beitrag in die FAQ verschieben könnte.
MfG
Happosai
-
Bevor es in die FAQ kommt wäre folgendes zu klären:
Das Problem: Die Initialisierung wird 2x vorgenommen.
Warum?
-
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
m_pSplitterWnd.Create(this,1,2,CSize(700,100), pContext);
m_pSplitterWnd.CreateView(0,0, RUNTIME_CLASS(CTree), CSize(175,100), pContext);
m_pSplitterWnd.CreateView(0,1, RUNTIME_CLASS(CList), CSize(500,100), pContext);return TRUE; //CFrameWnd::OnCreateClient(lpcs, pContext);
}
Dabei haut's ihn aber immer beim Start des Progs rausHast Du es mal so versucht:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { m_pSplitterWnd.CreateStatic(this, 1, 2,CSize(700,100), pContext); m_pSplitterWnd.CreateView(0,0, RUNTIME_CLASS(CTree), CSize(175,100), pContext); m_pSplitterWnd.CreateView(0,1, RUNTIME_CLASS(CList), CSize(500,100), pContext); return TRUE; //CFrameWnd::OnCreateClient(lpcs, pContext); }
Ich hatte nämlich noch keine Probleme damit. Auch nicht ohne Dokument, und so eine komplizierte Lösung scheint mir im Moment noch mächtig übertrieben.
-
Hab schon ein neues Topic erstellt, extra für die FAQ:
SDI ohne Doc/View splittenBTW: Wo ist bei deiner Lösung unten jetzt was anders als bei meiner???
Happosai
-
Create -> CreateStatic
-
Original erstellt von RenéG:
Create -> CreateStaticSorry, hab'sch übersehen.
MFG
Happosai