Hlavní
Hlavní strana
Seznam článků
Nejčtenější články
Progres e-mailem
Visual C++ FAQ

Seriály
COM
ISAPI

banner2.gif (2546 bytes)

Nenechte si ujít
Neobdélníková okna
Tisk bez Preview
MFC a DLL
Logo v MDI ploše
Kouzla s kombo-boxem
Výjimky v C++

banner.gif (3305 bytes)

Zakázaná stránka v PropertySheet Radek Pavienský
02.11.1999
[Hlavní stránka]  |  [Rubrika]  |  [Download]

V tomto článku si ukážeme, jak zakázat stránku v PropertySheet. Jde nám o to, aby se uživatel nemohl na danou stránku dostat žádným způsobem a aby záložka svázaná s touto stránkou byla vykreslena jiným způsobem.Například jako je tomu na následujícím obrázku:

rp_011199_propsheet_01.gif (2357 bytes)

Abychom dosáhli požadovaného stavu musíme zajistit následující akce:

1. Podědit potomka ze třídy CPropertySheet.
2. Rozšířit třídu o členskou proměnnou, která bude držet informace o zakázaných stránkách.
3. Zajistit vykreslování zakázaných záložek
4. Zakázat přepnutí do zakázané záložky.

První bod asi nepotřebuje komentář, a proto se hned podíváme na další bod. Ke každé stránce si budeme držet informace, zda je povolená, či ne. K tomu nám poslouží pole bajtů, kde každé stránce bude odpovídat jeden záznam. Pro přístup k poli zavedeme metody SetEnableState a GetEnableState. Abychom si zjednodušili práci, budou se nenastavené položky chápat, jako by stránky byly povolené. Metody tedy budou vypadat takto:

void PRO_PropertySheet::SetEnableState(int iPage, bool bEnable)
{
  // Pokud je stránka mimo rozsah, přidáme informace o daších
  // stránkách.
  if (iPage >= m_oStatusArray.GetSize())
  {
    // Stav nastavujeme na povolen
    while (iPage >= m_oStatusArray.GetSize())
      m_oStatusArray.Add(1);
  }
  
  // Nastavíme konkrétní stránku
  m_oStatusArray.SetAt(iPage, (BYTE)bEnable);
}

//-----------------------------------------------------------------

bool PRO_PropertySheet::GetEnableState(int iPage)
{
  // Pokud jsme mimo nastavení, tak se tváříme, jako že je stránka
  // povolena.
  if (iPage>=m_oStatusArray.GetSize())
    return true;
  
  // Jinak poctivě získáme stav
  return m_oStatusArray.GetAt(iPage) ? true : false;
}

Snad je vše jasné z komentářů. Navržený algoritmus není připraven na odstraňování stránek, ale protože to není příliš běžná akce, bude nám to (zatím) takto vyhovovat.

Nyní můžeme přistoupit ke třetímu bodu - vykreslování. Abychom mohli převzít vykreslování, je třeba nastavit prvek TabControl, který třída CPropertySheet obsahuje, tak aby jsme se o vykreslování starali my, třeba následovně:

BOOL PRO_PropertySheet::OnInitDialog() 
{
  // Voláme metodu předka
  BOOL bResult = CPropertySheet::OnInitDialog();
  
  // Získáme ukazatel na prvek TabControl  
  CTabCtrl *poTabCtrl = GetTabControl();   
  if (!poTabCtrl->GetSafeHwnd())
    return FALSE;
  
  // Získáme původní styl
  DWORD dwStyle = GetWindowLong(poTabCtrl->GetSafeHwnd(), GWL_STYLE);
  
  // Nastavíme styl, že se o vykreslování staráme my
  dwStyle |= TCS_OWNERDRAWFIXED;
  ::SetWindowLong(poTabCtrl->GetSafeHwnd(), GWL_STYLE, dwStyle);

  return bResult;
}

V okamžiku, kdy nastavíme styl OwnerDraw, se musíme o vykreslení postarat. Systém zajistí, že pro každou vykreslovanou záložku je volána metoda OnDrawItem, kde zajistíme vykreslení textu. Pokud zjistíme, že záložka vede na zakázanou stránku, zobrazíme text "zakázaně", tedy vmáčkle šedě. Toto se snadno zajistí dvojitým vykreslením na posunutých souřadnicích, jak je vidět ze zdrojového textu:

void PRO_PropertySheet::OnDrawItem(
  int nIDCtl, 
  LPDRAWITEMSTRUCT lpDIS) 
{
  // Převedeme si HDC na CDC abychom mohli používat metody
  CDC oDC;
  oDC.Attach(lpDIS->hDC);

  CRect rcItem = lpDIS->rcItem;

  // Pokud zobrazujeme vybranou záložku, je třeba zrušit bílou 
  // oddělovací čáru. Toho docílíme třeba vymazáním celé záložky
  if (lpDIS->itemState == ODS_SELECTED)
  {
    oDC.FillSolidRect(&rcItem, RGB(192,192,192));
    rcItem.OffsetRect(11);
  }
  else
    rcItem.OffsetRect(22);
  
  // Získáme text záložky, který je uložen přímo v informacích
  // prvku TabControl.  
  CString strText;  
  TC_ITEM oItem;
  oItem.mask = TCIF_TEXT;
  oItem.pszText = strText.GetBuffer(200);
  oItem.cchTextMax = 200;
  GetTabControl()->GetItem(lpDIS->itemID, &oItem);
  strText.ReleaseBuffer();
  
  // Pokud je stránka povolena, záložku vykreslíme jako běžný text  
  if (GetEnableState(lpDIS->itemID))
  {
    oDC.DrawText(strText, rcItem, 
      DT_SINGLELINE | DT_CENTER | DT_VCENTER);
  }
  else
  {
    // Pokud je stránka zakázána, 
    
    // tak vykreslíme nejprve text bíle
    oDC.SetBkMode(TRANSPARENT);  
    COLORREF clrOld = oDC.GetTextColor();
    oDC.SetTextColor(RGB(255,255,255));
    oDC.DrawText(strText, rcItem, 
      DT_SINGLELINE | DT_CENTER | DT_VCENTER);

    // a poté tmavě šedě na souřadnicích o bod vedle v obou osách
    oDC.SetTextColor(RGB(128,128,128));
    rcItem.OffsetRect(-1-1);
    oDC.DrawText(strText, rcItem, 
      DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    oDC.SetTextColor(clrOld);
  }
  
  // Oddělíme třídu CDC a HDC, protože jinak by došlo ke 
  // zrušení kontextu zařízení.
  oDC.Detach();
  
}

Poslední co musíme zajistit, je aby se uživatel nemohl přepnout do zakázané stránky. Toto se řeší pomocí notifikačních zpráv. Bohužel tyto zprávy chodí pouze v případě, že dojde k přepnutí myší, pomocí klávesnice (CTRL + TAB, atp.) budeme muset reakci řešit jinak.

BOOL PRO_PropertySheet::OnNotify(
  WPARAM wParam, 
  LPARAM lParam, 
  LRESULT* pResult) 
{
  NMHDR* pnmh = (NMHDR*)lParam;
  
  // Tato notifikace určuje, že dojde k přepnutí stránek. Uložíme si
  // posledně vybranou stránku, abychom se měli kam přepnout v 
  // případě, že se nepodaří přepnout na cílovou stránku.
  if (pnmh->code == TCN_SELCHANGING)
  {
    int iPage = GetTabControl()->GetCurSel();
    m_nLastSelPage = iPage;
    *pResult = TRUE;
  }

  // Tato notifikace určuje, že došlo k přepnutí stránky.
  if (pnmh->code == TCN_SELCHANGE)
  {
    // Pokud přepnutá stránka je zakázaná, 
    int iPage = GetTabControl()->GetCurSel();

    // tak se přepneme na tu, která byla aktivní před přepnutím.
    if (!GetEnableState(iPage))
      SetActivePage(m_nLastSelPage);
  }
  
  // Dáme šanci předkovi
  return CPropertySheet::OnNotify(wParam, lParam, pResult);
}

Poslední problém, který musíme vyřešit, se týká přepnutí pomocí klávesnice. Tato akce je vytvářena v metodě PreTranslateMessage předka. Tuto metodu kompletně překryjeme a napíšeme vlastní správu přepínání stránek, kde zakážeme přepnutí do nepovolených stránek, například takto:

BOOL PRO_PropertySheet::PreTranslateMessage(MSG* pMsg) 
{
  ASSERT_VALID(this);

  // allow tooltip messages to be filtered
  if (CWnd::PreTranslateMessage(pMsg))
    return TRUE;
  
  // Pokud je to "naše" kombinace...
  if (pMsg->message == WM_KEYDOWN && 
      GetAsyncKeyState(VK_CONTROL) <0 &&
    (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR 
    || pMsg->wParam == VK_NEXT)
    )
  {
    // Zjistíme vybranou stránku
    int iPage = GetTabControl()->GetCurSel();
    int nDirect; // Proměnná představuje směr posunu
    
    // Zjistíme směr posunu     
    nDirect = +1;
    if (pMsg->wParam == VK_TAB && GetAsyncKeyState(VK_SHIFT) < 0)
        nDirect = -1;
    if (pMsg->wParam == VK_PRIOR)
        nDirect = -1;
    
    // Posun na další (předchozí) stránku    
    iPage += nDirect;            
    
    // Pokud jsme příliš daleko (blízko) upravíme na správná čísla
    if (iPage >= GetPageCount())
      iPage = 0;

    if (iPage < 0)
      iPage = GetPageCount()-1;
    
    // Test, zda je stránka povolena
    if (!GetEnableState(iPage))
    {
      // Pokud ne, tak nalezneme nejbližší povolenou v 
      // požadavaném směru.
      int iIndex = iPage;
      while (!GetEnableState(iPage))
      {
        iPage += nDirect;
        if (iPage >= GetPageCount())
          iPage = 0;
        if (iPage < 0)
          iPage = GetPageCount()-1;
      }
    }
    // Nastavíme stránku
    SetActivePage(iPage);
    // TRUE, jako že jsme zprávu obsloužili
    return TRUE;
  }

  // handle rest with IsDialogMessage
  return PreTranslateInput(pMsg);
}

Tímto jsme splnili poslední krok. Třída má ještě pár nedostatků, jejichž řešení ale není nic náročného, to jistě každý zvládne:

1. Není ošetřeno odebírání stránek a jejich synchronizace s naším polem nastavení.
2. V záložkách se vykresluje jenom text.
3. Zakázaná stránka lze vybrat programově.

Kompletní zdrojový text a příklad použití lze nalézt v sekci Download tohoto článku.


Podobné články:

Kdo Otázka nebo připomínka

Prohlížení příspěvků nebo nový příspěvek

O firmě... Kontakt Ostatní