Visual C++ |
L'article presentat aquí està inspirat en l'article Banner horitzontal per rotació de caracteres., el qual, com el seu nom indica, consisteix a anar movent els caràcters de la cadena mostrada del principi al final. Aquí es presenta una variant de l'esmentat article que adquireix un aspecte més professional.
Les variants són:
El desenvolupament d'aquest control és molt semblant a l'esmentat anteriorment, per la qual cosa igual com podrien ser omesos alguns detalls d'altres podrien ser repetits.
Derivem una classe anomenada CHScrollingBannerStc de < b>CStatic d'aquesta manera podrem
utilitzar-la de forma genèrica en qualsevol aplicació simplement afegint aquesta classe al nostre projecte.
class CHScrollingBannerStc : public CStatic
{
....
public:
void AddString(CString& sArray);
....
protected:
// Generated message map functions
//{{AFX_MSG(CHScrollingBannerStc)
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnDestroy();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CString m_sTexto; // text que es mostra actualment
CStringArray m_sStrings; // cadenes de text a mostrar
RECT m_rect; // rectangle del control
bool m_flagTimer; // indica si el temporitzador ha estat establert
int m_nClip; // quantitat del rectangle a decréixer
int m_index; // índex de la cadena en curs
};
Per afegir les cadenes de text que volem mostrar en el nostre control, utilitzem la funció AddString a la qual se li passa una cadena de tipus CString i aquesta s'afegeix en el vector de cadenes m_sStrings.
#define SCROLL 1 // 1 = un píxel; 2 = 2 píxels; ...
#define MSEGUNDOS 40 // cada 40 mil·lisegons
void CHScrollingBannerStc::AddString(CString& sArray)
{
if (!m_flagTimer)
{
// només s'executarà la primera vegada que s'afegeixi una cadena de text
if (SetTimer(ID_TIMER, MSEGUNDOS, NULL) != NULL)
m_flagTimer = true;
GetClientRect(&m_rect);
// assignem la primera cadena de text a mostrar
m_sTexto = sArray;
}
// anem afegint cadenes a mostrar
m_sStrings.Add(sArray);
}
L'identificador ID_TIMER ha estat afegit utilitzant el diàleg Resource Symbols que s'accedeix a través del menú View. D'aquesta manera s'assigna el valor assignat per defecte evitant així l'existència de valors repetits en els identificadors.
Cal afegir el gestor OnTimer per al missatge WM_TIMER. Aquí serà on actualitzem el text i obligarem que es repinti de nou. Aquesta funció simplement augmenta el valor de m_nClip utilitzat per anar fent decréixer el rectangle on començarà a dibuixar-se el text.
void CHScrollingBannerStc::OnTimer(UINT nIDEvent)
{
if (nIDEvent == ID_TIMER)
{
// anem augmentant el rectangle a dibuixar
m_nClip += SCROLL;
Invalidate();
}
CStatic::OnTimer(nIDEvent);
}
Finalment només ens queda la funció OnPaint per a la gestió del missatge WM_PAINT. Aquesta funció és la que gestiona l'efecte de desplaçament del text.
void CHScrollingBannerStc::OnPaint()
{
RECT rect;
CWnd *pWndParent = NULL;
CPaintDC dc(this);
CDC memDC;
CBitmap bitmap, *oldBitmap;
SIZE size;
// seleccionem el color de fons per defecte del control estàtic
CBrush bBrush(::GetSysColor(COLOR_3DFACE));
CBrush* pOldBrush;
// obtenim el pare del control, generalment un diàleg
pWndParent = GetParent();
if (pWndParent == NULL)
return;
// creem un context de dispositiu en memòria compatible
// amb el del control, en el qual es dibuixarà el text
if (!memDC.CreateCompatibleDC(&dc))
{
TRACE0("Error en crear DC en memòria");
return;
}
// inicialitzem el bitmap que tindrà la imatge final a copiar en el control
if (!bitmap.CreateCompatibleBitmap(&dc, m_rect.right, m_rect.bottom))
{
TRACE0("Error en inicialitzar bitmap en memòria");
return;
}
// seleccionem la font per defecte de la finestra pare
memDC.SelectObject(pWndParent->GetFont());
// seleccionem el bitmap on pintarem el text
oldBitmap = memDC.SelectObject(&bitmap);
// seleccionem el pinzell per esborrar el contingut del rectangle
pOldBrush = memDC.SelectObject(&bBrush);
// omplim el rectangle amb el color seleccionat
memDC.FillRect(&m_rect, &bBrush);
// seleccionem el rectangle on DrawText() començarà a pintar el text,
// això és el que genera l'efecte de desplaçament del text, és a dir,
// indiquem a DrawText() que pinti el text un píxel a l'esquerra cada vegada
rect = m_rect;
rect.left = rect.right - m_nClip;
// pintem el text en memòria
memDC.SetBkMode(TRANSPARENT);
memDC.DrawText(m_sTexto, &rect, DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE);
// copiem el contingut del DC en memòria al DC del control
dc.BitBlt(0, 0, m_rect.right, m_rect.bottom, &memDC, 0, 0, SRCCOPY);
// hem de calcular quan ha acabat de passar la cadena. La següent funció
// calcula el rectangle exacte de la cadena segons el DC, només hem de calcular
// que m_nClip sigui major o igual que la mida del rectangle del control (amplada)
// més la mida del rectangle de la cadena, perquè acabi de passar del tot
GetTextExtentPoint32(memDC.GetSafeHdc(), m_sTexto, m_sTexto.GetLength(), &size);
if (m_nClip >= m_rect.right + size.cx)
{
// ja ha acabat de passar una cadena
m_nClip = 0;
// si s'ha mostrat l'última, comencem de nou amb la primera
if (++m_index == m_sStrings.GetSize())
m_index = 0;
// seleccionem la cadena següent o tornem a la primera
// si han acabat de passar totes
m_sTexto = m_sStrings.GetAt(m_index);
}
// deixem les coses com estaven i eliminem els objectes
memDC.SelectObject(oldBitmap);
memDC.SelectObject(&pOldBrush);
memDC.DeleteDC();
bitmap.DeleteObject();
bBrush.DeleteObject();
}
Si en lloc de pintar el text en memòria amb DrawText el pintéssim directament sobre el context de dispositiu del control, en els extrems estaríem pintant fora del rectangle del control, això podria ser conflictiu. En canvi, si pintem en el context de dispositiu en memòria, encara que pintem fora del rectangle no importa, ja que després es fa una còpia del que conté el rectangle que ens interessa i el que pogués quedar fora d'aquest rectangle no ens afectarà per a res.
La funció GetTextExtentPoint32 és molt útil per calcular el rectangle exacte del text. Utilitza el tipus de lletra seleccionada per calcular les dimensions de la cadena. D'aquesta manera, com es detalla en els comentaris, podem saber quan ha acabat de passar la cadena de text que es mostra actualment per mostrar la següent.
El nostre control Banner ja està acabat, només ens falta crear una variable membre m_ctrlBanner de tipus CHScrollingBannerStc en el diàleg que conté el nostre control i cridar a la nostra funció AddString per a cada cadena que vulguem afegir al control.
BOOL CHScrollingBannerDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_ctrlBanner.AddString(CString("Primer text")
m_ctrlBanner.AddString(CString("Segon text")
m_ctrlBanner.AddString(CString("Tercer text")
return TRUE;
}
| Fonts | ... | hscrolling_banner_sources.zip 19 Kb |
| Vegi's també | ... | Banner horitzontal per rotació de caràcters |
| Banner vertical per desplaçament del text |