Enumerarea prin ferestrele child dintr-un MDI

Intrebari legate de programarea cu biblioteci precum MFC, ATL, WTL si GDI+.
Post Reply
User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Enumerarea prin ferestrele child dintr-un MDI

Post by Silviu Ardelean » 25 Jan 2013, 14:44

M-ar interesa o metoda eleganta si eficienta de-a enumera ferestrele child deschise dintr-un MDI in CMainFrame.
Folosesc VS 2010 iar o varianta de genul asta risc sa ma lovesc de o problema pe care am descris-o aici si sa obtin crash la atasare.
O varianta mai ciobaneasca ar fi sa am un vector de CChildFame pointers ce sa se populeze de fiecare data cand cate un CChildFrame e creeat (urmand a fi eliminat pointerul pe CChildFrame::OnClose()) si asa as putea enumera prin ferestrele child de ex. pe CMainFrame::OnClose().

Sunt deschis la alte variante. :)



Viorel
Microsoft MVP
Microsoft MVP
Posts: 291
Joined: 13 Jul 2007, 12:26

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by Viorel » 25 Jan 2013, 15:29

Dacă vrei să enumeri toate ferestrele asociate documentelor deschise, atunci începe cu CWinApp::GetFirstDocTemplatePosition și GetNextDocTemplate. Pentru fiecare șablon, enumeră documentele folosind GetFirstDocPosition și GetNextDoc. Apoi poți enumera ferestrele view cu GetFirstViewPosition și GetNextView. Fiecare view este de obicei inserat într-un CChildFrame, deci execută „CChildFrame * f = DYNAMIC_DOWNCAST(CChildFrame, view->GetParentFrame())”, exclude repetările și valorile nule.

Cred că poți folosi și metoda descrisă în documentația citată, convertind fiecare CMDIChildWnd spre CChildFrame folosind STATIC_DOWNCAST.

mesajflaviu
Membru++
Membru++
Posts: 689
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by mesajflaviu » 25 Jan 2013, 16:12

Viorel, ii explici lui Silviu, si nu inteleg eu :biggrin:, ca sa vezi unde dai si unde crapa ... Adica la enumerarea fiecarui view, se verifica parintele CChildFrame daca e NULL sau a mai fost ?
Last edited by mesajflaviu on 25 Jan 2013, 16:13, edited 1 time in total.

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3776
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by Ovidiu Cucu » 25 Jan 2013, 16:13

Silviu Ardelean wrote:O varianta mai ciobaneasca ar fi sa am un vector de CChildFame pointers ce sa se populeze de fiecare data cand cate un CChildFrame e creeat (urmand a fi eliminat pointerul pe CChildFrame::OnClose()) si asa as putea enumera prin ferestrele child de ex. pe CMainFrame::OnClose().
Desigur, un cioban care paste oile pe deal la GoF, asa ar face... :D

Asemanator cu ce-a spus deja Viorel:
Cea mai buna metoda, zic eu, ar cea din codexpert blog: Enumerate documents in MDI applications.
Odata ce am lista cu documentele, e floare la ureche sa obtin MDI Child frame-ul corespunzator pentru fiecare.
La articolul Custom MDI More Windows Dialog, gasesti si un proiect demo in care poti arunca o privire ca sa vezi ce si cum e de facut.

Ai putea sa le obtii si mai direct, sa zicem iei MDI child-ul activ cu CMDIFrameWnd::MDIGetActive apoi te plimbi cu CWnd::GetWindow ca sa-i gasesti fratiorii. Aparent e mai simplu insa nu cred ca si mai bine. Trebuie sa fii atent ca CWnd::GetWindow intoarce un pointer "temporar", pe care nu poti sa-l pui "la pastrare". Din cuza asta, ar trebui sa umpli tu lista cu obiectele tale "permanente" si bineniteles, sa faci la urma curatenie...

Si-or mai fi, insa eu iti recomand prima.

User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by Silviu Ardelean » 25 Jan 2013, 16:21

Elegant si rezolvat. Multumesc Viorel!
mesajflaviu wrote:Viorel, ii explici lui Silviu, si nu inteleg eu :biggrin:, ca sa vezi unde dai si unde crapa ... Adica la enumerarea fiecarui view, se verifica parintele CChildFrame daca e NULL sau a mai fost ?
Flaviu, cred ca stii, odata ce ai view-ul cu un GetParentFrame() ai si pointerul la CChildFrame. :)
Ovidiu Cucu wrote:Asemanator cu ce-a spus deja Viorel:
Cea mai buna metoda, zic eu, ar cea din codexpert blog: Enumerate documents in MDI applications.
Odata ce am lista cu documentele, e floare la ureche sa obtin MDI Child frame-ul corespunzator pentru fiecare.
Intr-adevar! :) Initial aveam in cap sa nu ajung la document, dar pana la urma nu-i mare "bai" daca ajung sa ma uit si ce documente am.

mesajflaviu
Membru++
Membru++
Posts: 689
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by mesajflaviu » 25 Jan 2013, 16:23

Flaviu, cred ca stii, odata ce ai view-ul cu un GetParentFrame() ai si pointerul la CChildFrame
Bun, dar nu inteleg la ce se referea Viorel cand spunea sa verifici repetarile si valorile nule ...
Last edited by mesajflaviu on 25 Jan 2013, 16:37, edited 1 time in total.

Viorel
Microsoft MVP
Microsoft MVP
Posts: 291
Joined: 13 Jul 2007, 12:26

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by Viorel » 25 Jan 2013, 16:25

mesajflaviu wrote:Viorel, ii explici lui Silviu, si nu inteleg eu , ca sa vezi unde dai si unde crapa ... Adica la enumerarea fiecarui view, se verifica parintele CChildFrame daca e NULL sau a mai fost ?
Dacă un CChildFrame conține mai multe view-uri (de ex. într-un CSplitterWnd), atunci unele view-uri for returna un CChildFrame care deja a fost enumerat. Prin urmare ar trebui verificat dacă CChildFrame a mai fost obținut.

Dacă un view nu este plasat într-un CChildFrame (caz probabil rar), atunci situația poate fi exclusă dacă după „CChildFrame * f = DYNAMIC_CAST(CChildFrame, view->GetFrameWindow())” se verifică ca f să nu fie nul.

mesajflaviu
Membru++
Membru++
Posts: 689
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by mesajflaviu » 25 Jan 2013, 16:31

M-am gandit eu ca la nivel de view se verifica CChildFrame daca e NULL sau dublura, dar nu ma gandeam la situatiile enumerate de tine, adica view-uri in CSplitterWnd sau view fara CChildFrame (ultima situatie n-am vazut-o niciodata pana acuma).

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3776
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by Ovidiu Cucu » 25 Jan 2013, 17:13

Acuma depinde si ce vrei sa faci. Sa zicem ca vrei doar sa numeri frame-urile MDI child, sa le intorci cu fundu-n sus sau orice altceva in afara de a le pune "la pastrare" atunci cel mai simplu faci ceva de genul:

Code: Select all

void CMainFrame::DoSomethingWithMDIClildFrames()
{
   BOOL bIsMax = FALSE;
   CWnd* pChildFrame = MDIGetActive(&bIsMax);
   while(NULL != pChildFrame)
   {
      if(pChildFrame->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)))
      {
         // do something with MDI child frame HERE!
      }
      pChildFrame = pChildFrame->GetWindow(GW_HWNDNEXT); // Next, please!
   }
}
Altfel mergi pe calea enumerarii document template -> document -> view, asa cum ti-a sugerat si Viorel si eu in primul raspuns.

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3776
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Enumerarea prin ferestrele child dintr-un MDI

Post by Ovidiu Cucu » 27 Jan 2013, 11:49

Sa incerc sa explic putin ce-i cu "pusul la pastrare" despre care am zis mai sus.

Framework-ul MFC tine, pentru fiecare thread UI, doua map-uri cu pointeri la obiecte CWnd: unul permanent si unul temporar.
Functiile gen CWnd::GetWindow, CWnd::GetDlgItem si altele asemeanatoare obtin mai intai un handle la fereastra (HWND) apeland functia WinAPI corespunzatoare (::GetWindow, ::GetDlgItem, etc), pe care apoi il paseaza lui CWnd::FromHanlde.
CWnd::FromHanlde face in felul urmator:
  1. Cauta HWND-ul in map-ul permanent. Daca il gaseste, intoarce pointer la CWnd-ul corespunzator.
  2. Daca nu, cauta HWND-ul in map-ul temporar. Daca il gaseste, intoarce pointer la CWnd-ul corespunzator.
  3. Daca nu, creaza un nou obiect CWnd si il adauga la map-ul temporar.


In CWinThread::OnIdle, care este apelata de framework atunci cand nu mai sunt mesaje in coada, se face curatenie, adica se goleste map-ul temporar. Este, daca vreti, un fel de "garbage collector".
De aceea, intotdeauna e bine de belit ochii in documentatie, iar atunci cand vedem ceva de genul...
MSDN
Return value:
[...]
The pointer may be temporary and should not be stored for later use.
...sa-i credem pe cuvant ca pointerii aia is pentru folosinta imediata si NU pentru pus la pastrare. Nici chiar daca acestia s-ar gasi in map-ul permanent tot nu e garantat ca intre timp unul dintre ei a fost sters de catre framework (sa zicem, cazul cand am pus la pastrare un pointer la un MDI child, iar intre timp userul a inchis frame-ul corespunzator).

Sper ca-i destul de clar... :)

Post Reply