|
Die Funktion DrawItem() ist verantwortlich für das Zeichnen der einzelnen Einträge der Listbox und muß auch das Ausgeben des Bitmaphintergrunds übernehmen.
Das Ausgaberechteck, der Index des betroffenen Eintrags wie auch der Ausgabekontext selbst werden in lpDrawItemStruct übergeben.
void CMyListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rcItem(lpDrawItemStruct->rcItem);
int nItem = lpDrawItemStruct->itemID;
CImageList* pImageList;
int nSavedDC = pDC->SaveDC();
Das Item-Image und die Statusinformationen werden in einer LV_ITEM-Struktur abgelegt.
LV_ITEM lvi;
lvi.mask = LVIF_IMAGE | LVIF_STATE;
lvi.iItem = nItem;
lvi.iSubItem = 0;
lvi.stateMask = 0xFFFF;
GetItem(&lvi);
Ob der Eintrag hervorgehoben anzuzeigen ist, ergibt sich anhand der Auswertung.
BOOL bHighlight = ((lvi.state & LVIS_DROPHILITED) ||
((lvi.state & LVIS_SELECTED) &&
((GetFocus() == this) ||
(GetStyle() & LVS_SHOWSELALWAYS))));
Die Rechtecke für die Ausgabe und der Labeltext ergeben sich aus:
CRect rcBounds, rcLabel, rcIcon;
GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
GetItemRect(nItem, rcLabel, LVIR_LABEL);
GetItemRect(nItem, rcIcon, LVIR_ICON);
CRect rcCol( rcBounds );
CString sLabel = GetItemText( nItem, 0 );
Die Labeltexte werden um einen bestimmten Offset verschoben angezeigt, wobei sich der Offset aus der Breite eines Leerzeichens ergibt.
int offset = pDC->GetTextExtent(_T(" "), 1).cx*2;
Hervorhebungen sind möglich für den Labelteil, das umhüllende Rechteck des Eintrags oder den gesamten Clientbereich des Eintrags.
CRect rcHighlight, rcClient;
int nExt;
switch( m_nHighlight )
{
case 0:
nExt = pDC->GetOutputTextExtent(sLabel).cx + offset;
rcHighlight = rcLabel;
if( rcLabel.left + nExt < rcLabel.right )
rcHighlight.right = rcLabel.left + nExt;
break;
case 1:
rcHighlight = rcBounds;
rcHighlight.left = rcLabel.left;
break;
case 2:
GetClientRect(&rcClient);
rcHighlight = rcBounds;
rcHighlight.left = rcLabel.left;
rcHighlight.right = rcClient.right;
break;
default:
rcHighlight = rcLabel;
}
Ist eine Bitmap gesetzt, wird diese als Hintergrund gezeichnet. Hierzu ist ein kompatibler Ausgabekontext notwendig. Der Clientbereich wird auch gleich vorweg bestimmt.
if( m_bitmap.m_hObject != NULL )
{
CDC tempDC;
tempDC.CreateCompatibleDC(pDC);
tempDC.SelectObject( &m_bitmap );
GetClientRect(&rcClient);
CRgn rgnBitmap;
CRect rcTmpBmp( rcItem );
rcTmpBmp.right = rcClient.right;
Falls es sich um den letzten Eintrag der Listbox handelt, muß die Updateregion bis zum unteren Rand erweitert werden.
if( nItem == GetItemCount() - 1 )
rcTmpBmp.bottom = rcClient.bottom;
rgnBitmap.CreateRectRgnIndirect(&rcTmpBmp);
pDC->SelectClipRgn(&rgnBitmap);
rgnBitmap.DeleteObject();
Für Ausgabegeräte, die eine logische Palette unterstützen, wird eine Palette realisiert, sofern eine solche vorhanden ist.
if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE &&
m_pal.m_hObject != NULL )
{
pDC->SelectPalette( &m_pal, FALSE );
pDC->RealizePalette();
}
Für die gesamte Ausgabefläche wird die Bitmap so oft neben- und untereinander plaziert, bis der gesamte Bereich überdeckt ist.
CRect rcFirstItem;
GetItemRect(0, rcFirstItem, LVIR_BOUNDS);
for( int i = rcFirstItem.left; i < rcClient.right;
i += m_cxBitmap )
for( int j = rcFirstItem.top; j < rcClient.bottom;
j += m_cyBitmap )
pDC->BitBlt( i, j, m_cxBitmap, m_cyBitmap, &tempDC,
0, 0, SRCCOPY );
}
Anschließend kann die Hintergrundfarbe gezeichnet werden:
if( bHighlight )
{
pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
pDC->FillRect(rcHighlight,
&CBrush(::GetSysColor(COLOR_HIGHLIGHT)));
}
else if( m_bitmap.m_hObject == NULL )
pDC->FillRect(rcHighlight,
&CBrush(::GetSysColor(COLOR_WINDOW)));
Als Clippingregion ergibt sich:
rcCol.right = rcCol.left +GetColumnWidth(0);
CRgn rgn;
rgn.CreateRectRgnIndirect(&rcCol);
pDC->SelectClipRgn(&rgn);
rgn.DeleteObject();
Nachdem die Ausgabe des Hintergrunds fertig ist, kann der Vordergrund gezeichnet werden, der aus den einzelnen Bestandteilen der Listview besteht. Zunächst wird das Symbol, sofern vorhanden, links plaziert.
if (lvi.state & LVIS_STATEIMAGEMASK)
{
int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1;
pImageList = GetImageList(LVSIL_STATE);
if (pImageList)
{
pImageList->Draw(pDC, nImage,
CPoint(rcCol.left, rcCol.top), ILD_TRANSPARENT);
}
}
Anschließend kann das normale Symbol wie auch das Overlay-Symbol gezeichnet werden.
pImageList = GetImageList(LVSIL_SMALL);
if (pImageList)
{
UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK;
pImageList->Draw(pDC, lvi.iImage,
CPoint(rcIcon.left, rcIcon.top),
(bHighlight?ILD_BLEND50:0) | ILD_TRANSPARENT | nOvlImageMask );
}
Die Labeltexte werden über die Methode DrawText() des DC-Objekts ausgegeben. Das Label für die erste Spalte besitzt die Attribute:
rcLabel.left += offset/2;
rcLabel.right -= offset;
pDC->DrawText(sLabel, -1, rcLabel,
DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP |
DT_VCENTER | DT_END_ELLIPSIS);
Die Label der übrigen Spalten werden ohne Hervorhebung gezeichnet.
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH;
if( m_nHighlight == 0 )
{
pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
}
rcBounds.right = rcHighlight.right > rcBounds.right ?
rcHighlight.right : rcBounds.right;
rgn.CreateRectRgnIndirect(&rcBounds);
pDC->SelectClipRgn(&rgn);
for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
{
rcCol.left = rcCol.right;
rcCol.right += lvc.cx;
Falls benötigt wird noch der Hintergrund gezeichnet.
if(m_bitmap.m_hObject == NULL && m_nHighlight == HIGHLIGHT_NORMAL)
pDC->FillRect(rcCol, &CBrush(::GetSysColor(COLOR_WINDOW)));
sLabel = GetItemText(nItem, nColumn);
if (sLabel.GetLength() == 0)
continue;
Die Textausrichtung für die Ausgabe der Texte via DrawText() wird wie folgt umgesetzt:
UINT nJustify = DT_LEFT;
switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
{
case LVCFMT_RIGHT:
nJustify = DT_RIGHT;
break;
case LVCFMT_CENTER:
nJustify = DT_CENTER;
break;
default:
break;
}
Anschließend kann die Textausgabe erfolgen.
rcLabel = rcCol;
rcLabel.left += offset;
rcLabel.right -= offset;
pDC->DrawText(sLabel, -1, rcLabel,
nJustify | DT_SINGLELINE | DT_NOPREFIX |
DT_VCENTER | DT_END_ELLIPSIS);
}
Besitzt der Eintrag den Fokus, muß letztlich noch ein Fokusrechteck eingesetzt werden.
if (lvi.state & LVIS_FOCUSED && (GetFocus() == this))
pDC->DrawFocusRect(rcHighlight);
Abschließend wird der Ausgabekontext wiederhergestellt und die Ausgabe ist beendet.
pDC->RestoreDC( nSavedDC );
}
|