[MFC] Cum setez dimensiunile frame-ului unui form view?

Despre MFC, ATL si alte biblioteci C++ de la Microsoft (forum moderat)
Post Reply
User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

[MFC] Cum setez dimensiunile frame-ului unui form view?

Post by Ovidiu Cucu » 19 Nov 2007, 18:41

Problema

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);

}
Note
  1. 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.
    Exista mai multe metode de transorma dialog units in pixeli, cea mai "comoda" fiind functia WinAPI MapDialogRect, respectiv daca utilizam MFC, CDialog::MapDialogRect. FAQ-ul de mai sus l-am scris ca raspuns la o problema pusa de cineva intr-un forum si care "pornise la drum" cautand o cale de a transforma dialog units in pixeli. Dupa cum s-a vazut, la problema data nua mai fost nevoie de transformare din moment ce CFormView::GetTotalSize imi da direct dimensiunea dialogului in pixeli.
  2. 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);
      }
      
  3. 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.
<< Back to MFC index



Post Reply