O chestie suparatoare si oarecum nefiresca intr-o aplicatie MDI/SDI avand view(uri) tip formview este ca pentru un dialog template relativ mare, userul trebuie sa faca scroll, pentru a avea acces la toate controalele din dialogul incorporat.
Chiar daca wizard-ul pune pentru MDI in functia CDerivedFormView::OnInitialUpdate un apel la CFormView::ResizeParentToFit (respectiv CFrameWnd::RecalcLayout plus CFormView::ResizeParentToFit pentru SDI), aceasta nu ajuta decat pentru view-uri bazate pe dialog template-uri de dimensiuni mici).
Deci...
Intrebare
Cum as putea face ca frame-ul parinte sa se dimensioneze exact dupa dimensiunea dialog template-ului, in asa fel incat sa nu mai apara scrollbar-urile?
Rezolvare pentru MDI
CFormView este derivata din CScrollView de unde mosteneste metoda GetTotalSize. Aceasta, pentru CFormView, da dimensiunile totale ale view-ului in pixeli (mai binezis dimensiunile dialogului care il incorporeaza), setate in CFormView::Create avand ca baza dimensiunile dialog template-ului din resurse (vezi nota 1).
Odata avand dimesiunile totale ale view-ului (dialogului) mai trebuie facute doua ajustari. Prima, de calculat dimensiunile ferestrei view incluzand border-ul si scrollbar-urile. (asta da chiar zona client necesara a frame-ului parinte). Cea de a doua, avand zona client a frame-ului, sa-i aflu dimensiunea totala (incluzand caption, border, etc). Nimic mai simplu, odata ce am la dispozitie functia numita CWnd::CalcWindowRect.
In cele din urma, chem SetWindowPos sa redimensionez frame-ul si... gata!
Exemplu
Code: Select all
void CMyMDIFormView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
// get the total view size
CSize size = GetTotalSize();
CRect rc(0, 0, size.cx, size.cy);
// calculate the necessary view window rectangle outside the scollbars
CalcWindowRect(rc, CWnd::adjustOutside);
// get the parent frame
CFrameWnd* pFrame = GetParentFrame();
// calculate the necessary frame window rectangle
pFrame->CalcWindowRect(rc);
// rezise the frame window
pFrame->SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(),
SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
}
Rezolvare pentru SDI
La SDI, schema exact ca cea de mai sus, nu functioneaza pentru ca view-ul imparte zona client a frame-ului cu controlbar-urile (toolbar, statusbar, etc). La prima vedere, ar trebui sa enumar controlbar-urile din frame, sa vad care-s docked, etc, etc... destul de urat.
Totusi, un mic artificiu imi permite sa-mi fac treaba la fel de simplu ca si la MDI: initial, las framework-ul sa-si "aranjeze" el view-ul si controlbar-urile, fac diferenta intre dimensiunile frame-ului si a view-ului initial, care o folosesc mai apoi ca sa redimendionez frame-ul pornind de la dimensiunile view-ului nou calculat.
Mai clar e in cod.
Exemplu
Code: Select all
void CMySDIFormView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
// get the parent frame
CFrameWnd* pFrame = GetParentFrame();
// first, let framework rearange the view given the docked control bars
pFrame->RecalcLayout();
// get the frame and initial view rectangle
CRect rcView, rcFrame;
GetWindowRect(rcView);
pFrame->GetWindowRect(rcFrame);
// make the difference
const int nXDiff = rcFrame.Width() - rcView.Width();
const int nYDiff = rcFrame.Height() - rcView.Height();
// get the total view size
CSize size = GetTotalSize();
CRect rc(0, 0, size.cx, size.cy);
// calculate the necessary view window rectangle outside the scollbars
CalcWindowRect(rc, CWnd::adjustOutside);
// rezise the frame window
pFrame->SetWindowPos(NULL, 0, 0,
rc.Width() + nXDiff, rc.Height() + nYDiff,
SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
}
- Dimensiunile date in dialog template-ul din resurse (vezi structura DLGTEMPLATE) sunt date in unitati dialog (dialog units) si nu in pixeli (screen units). Acestea reprezinta:
- pe orizontala, o patrime din media latimii caracterelor din fontul folosit.
- pe verticala, o optime din media inaltimii caracterelor din font.
- Odata ce am fit-uit exact frame-ul pe dimensiunea dialogului, foarte posibil e ca sa apara si cerinta ca acesta sa nu mai poata si redimensionat de catre user si sau sa nu mai poata fi maximizat. Obtinem astfel un fel de aplicatie "dialog-based-like", beneficiind in acelasi timp de facilitatile "gata preparate" ale framework-ului MDI/SDI (menu, docking toolbar, document-view support, etc, etc).
Acasta se face foarte simplu in functia virtuala PreCreateWindow a frame-ului respectiv.- MDI Application
Code: Select all
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs) { cs.style &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX); return CMDIChildWnd::PreCreateWindow(cs); }
- SDI Application
Code: Select all
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { cs.style &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX); return CFrameWnd::PreCreateWindow(cs); }
- MDI Application
- Problema scroll-ului intr-un form view face multi incepatori in ale MFC-ului sa renunte la MDI/SDI si sa abordeze aplicatia cu dialoguri (dialog-based). OK, no problem, si alea pot fi aplicatii excelente, numai ca incep dupa aceea sa intrebe: da' cum ii pun un menu cu user-interface update support, da' cum ii pun dockable toolbar, da' cum...? Ori MDI/SDI framework ofera toate astea gata preparate iar mici customizari "cosmetice" dupa pohta fiecaruia sunt floare la ureche si la indemana orisicui.