01
Aug
2007

Functia AnimateWindow() ofera efecte speciale pentru afisarea si ascunderea unei ferestre (rulare, alunecare, colaps, expandare, si palire alfa). Aceasta functie e o alternativa la ShowWindow(). In cazul unei ferestre modale, ShowWindow() nu este apelat direct de utilizator ci de framework, ceea ce inseamna ca nu se poate substitui cu AnimateWindow() intr-un mod trivial. Acest articol explica care este abordarea pentru a realiza acest lucru.

Daca rulati pas cu pas in functia DoModal() veti observa codul de mai jos:

TRY
{
   // create modeless dialog
   AfxHookWindowCreate(this);
   if (CreateDlgIndirect(lpDialogTemplate,
      CWnd::FromHandle(hWndParent), hInst))
   {
      if (m_nFlags & WF_CONTINUEMODAL)
      {
         // enter modal loop
         DWORD dwFlags = MLF_SHOWONIDLE;
         if (GetStyle() & DS_NOIDLEMSG)
            dwFlags |= MLF_NOIDLEMSG;
         VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
      }
         // hide the window before enabling the parent, etc.
      if (m_hWnd != NULL)
      {
         SetWindowPos(NULL, 0, 0, 0, 0,
            SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|
            SWP_NOZORDER);
      }
   }
}
CATCH_ALL(e)
{
   DELETE_EXCEPTION(e);
   m_nModalResult = -1;
}
END_CATCH_ALL

RunModalLoop() este functia unde este implementata bucla de mesaje, si unde sunt localizate apelurile la ShowWindow(). Din pacate aceasta functie nu e virtuala, ceea ce inseamna ca nu poate fi suprascrisa, dar poate fi ascunsa, prin crearea intr-o clasa derivata din CDialog a unei functii cu acelasi nume, si a apelarii ei din functia DoModal() suprascrisa. Codul din DoModal() nu necesita modificari, dar pentru a putea apela RunModalLoop() din clasa derivata, este necesara suprascrierea acestei metode.

In aplicatia demo ce poate fi descarcata am creat o clasa numita CAnimDialog derivata din CDialog, cu doi constructori care sa corespunda constructorilor lui CDialog, dar cu parametrii aditionali pentru a specifica efectul si timpul de rulare al sau in cazul afisarii sau ascunderii ferestrei.

explicit CAnimDialog(LPCTSTR lpszTemplateName, CWnd* pParent = NULL,
   DWORD milisecsshow = 200, DWORD stylesshow = AW_SLIDE|AW_BLEND,
   DWORD milisecshide = 200, DWORD styleshide = AW_SLIDE|AW_BLEND);
explicit CAnimDialog(UINT nIDTemplate, CWnd* pParent = NULL,
   DWORD milisecsshow = 200, DWORD stylesshow = AW_SLIDE|AW_BLEND,
   DWORD milisecshide = 200, DWORD styleshide = AW_SLIDE|AW_BLEND);

Codul din DoModal() este copiat din clasa CDialog. La fel si in cazul lui RunModalLoop(), cu exceptia apelurilor la functia ShowWindow() care au fost inlocuite cu AnimateWindow.

m_dwStylesShow &= ~AW_HIDE;
AnimateWindow(m_dwMilisecondsShow, AW_ACTIVATE|m_dwStylesShow);

Inaintea apelului lui AnimateWindow e bine de verificat ca AW_HIDE nu a fost specificat din gresala. Codul complet pentru RunModalLoop() este listat mai jos:

int CAnimDialog::RunModalLoop(DWORD dwFlags)
{
   ASSERT(::IsWindow(m_hWnd));            // window must be created
   ASSERT(!(m_nFlags & WF_MODALLOOP));    // window must not already
                                          // be in modal state

   // for tracking the idle time state
   BOOL bIdle = TRUE;
   LONG lIdleCount = 0;
   BOOL bShowIdle = (dwFlags & MLF_SHOWONIDLE) && !(GetStyle()
      & WS_VISIBLE);
   HWND hWndParent = ::GetParent(m_hWnd);
   m_nFlags |= (WF_MODALLOOP|WF_CONTINUEMODAL);
   MSG *pMsg = AfxGetCurrentMessage();

   // acquire and dispatch messages until the modal state is done
   for (;;)
   {
      ASSERT(ContinueModal());

      // phase 1: check to see whether we can do idle work
      while (bIdle &&
         !::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
      {
         ASSERT(ContinueModal());

         // show the dialog when the message queue goes idle
         if (bShowIdle)
         {
            m_dwStylesShow &= ~AW_HIDE;
            AnimateWindow(m_dwMilisecondsShow,
                          AW_ACTIVATE|m_dwStylesShow);
            UpdateWindow();
            bShowIdle = FALSE;
         }

         // call OnIdle while in bIdle state
         if (!(dwFlags & MLF_NOIDLEMSG) && hWndParent
             != NULL && lIdleCount == 0)
         {
            // send WM_ENTERIDLE to the parent
            ::SendMessage(hWndParent, WM_ENTERIDLE, MSGF_DIALOGBOX,
                          (LPARAM)m_hWnd);
         }
         if ((dwFlags & MLF_NOKICKIDLE) ||
            !SendMessage(WM_KICKIDLE, MSGF_DIALOGBOX, lIdleCount++))
         {
            // stop idle processing next time
            bIdle = FALSE;
         }
      }

      // phase 2: pump messages while available
      do
      {
         ASSERT(ContinueModal());

         // pump message, but quit on WM_QUIT
         if (!AfxPumpMessage())
         {
            AfxPostQuitMessage(0);
            return -1;
         }

         // show the window when certain special messages rec'd
         if (bShowIdle &&
            (pMsg->message == 0x118 || pMsg->message == WM_SYSKEYDOWN))
         {
            m_dwStylesShow &= ~AW_HIDE;
            AnimateWindow(m_dwMilisecondsShow,
                          AW_ACTIVATE|m_dwStylesShow);
            UpdateWindow();
            bShowIdle = FALSE;
         }

         if (!ContinueModal())
            goto ExitModal;

         // reset "no idle" state after pumping "normal" message
         if (AfxIsIdleMessage(pMsg))
         {
            bIdle = TRUE;
            lIdleCount = 0;
         }

      } while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
   }

ExitModal:
   m_nFlags &= ~(WF_MODALLOOP|WF_CONTINUEMODAL);
   return m_nModalResult;
}

Pentru a trata cazul in care fereastra este ascunsa, trei metode trebuie suprascrise:

void CAnimDialog::OnClose()
{
   m_dwStylesHide &= ~AW_ACTIVATE;
   AnimateWindow(m_dwMilisecondsHide, AW_HIDE|m_dwStylesHide);

   CDialog::OnClose();
}

void CAnimDialog::OnOK()
{
   m_dwStylesHide &= ~AW_ACTIVATE;
   AnimateWindow(m_dwMilisecondsHide, AW_HIDE|m_dwStylesHide);

   CDialog::OnOK();
}

void CAnimDialog::OnCancel()
{
   m_dwStylesHide &= ~AW_ACTIVATE;
   AnimateWindow(m_dwMilisecondsHide, AW_HIDE|m_dwStylesHide);

   CDialog::OnCancel();
}

In acest caz, inaintea apelului la AnimateWindow() trebuie asigurat ca AW_ACTIVATE nu este specificat, intrucat fereastra trebuie ascunsa, nu afisata.

Folosirea acestei clase, CAnimDialog, este foarte simpla: in loc sa ca o clasa sa fie derivata din CDialog, ea trebuie derivata din CAnimDialog (aveti grija sa includeti fisierul AnimDialog.h in header-ul clasei derivate). In plus, orice referire la CDialog trebuie inlocuita cu CAnimDialog.

In aplicatia mea demo, CConcreteAnimDialog este o clasa derivata din CAnimDialog. Afisarea ei cu un efect de alunecare si palire, si ascunderea ei cu un efect de rulare de jos in sus se face simplu ca mai jos:

CConcreteAnimDialog dlg(200, AW_SLIDE|AW_BLEND,
                        200, AW_SLIDE|AW_VER_NEGATIVE);

dlg.DoModal();

Atentie: nu toate combinatiile posibile de stiluri sunt admise. Cititi cu atentie documentatia inainte sa le folositi. [phpBB Debug] PHP Warning: in file [ROOT]/phpbb/db/driver/mysqli.php on line 317: mysqli_free_result(): Couldn't fetch mysqli_result