Board index » Visual Studio » Owner-draw CListCtrl selection problem
|
singlgl1
|
Owner-draw CListCtrl selection problem
Visual Studio243
Hello all, I have a class named CMyListCtrl derived from CListCtrl using LVS_REPORT with multiple columns. It is owner draw and overrides both OnPaint() and DrawItem(). OnPaint() is called to draw a series of rectangles that create a grid-like effect. Then it iterates through all items, populating a DRAWITEMSTRUCT and calling DrawItem(). Selections are drawn using a distinct background color for the row (which must be done in OnPaint() so as to preserve the grid effect) and a different text color (done in DrawItem() ). This works great when the user selects any item on the list--except the very first one! If the user selects the first item, the previously selected item is deselected and drawn with the non-selection colors, but the new selection (index 0) is not drawn with the selection colors. As soon as I switch the focus to another window or control, however, the item is repainted correctly and we see it properly highlighted. Using breakpoints, I can actually see the correct colors being selected and the appropriate drawing functions ( FillSolidRect() and DrawText() ) being called in OnPaint() and DrawItem(), and yet the item remains the same! It's like it is not being repainted at all. Because when execution hits the breakpoint the focus switches to the Visual Studio window, a second paint message is sent to the control. This time it gets redrawn correctly. It is almost like something is "locking" or maybe even clipping the area of the first item when the user selects it, but switching the focus seems to somehow unlock it. I noticed that if I override only DrawItem and just let the base-class's OnPaint() do its thing, everything works properly. I also notice the base class only repaints the old selection and the new selection when the user clicks on an item, while my version always repaints all items regardless of what triggered OnPaint()--quite frankly I don't even know how to tell what triggered the painting so I have no option but to redraw everything. Does this seem bizarre to anyone or is it just me? I included the code for DrawItem() and OnPaint() below--sorry, they are still a little rough... Any help is appreciated! Thanks, Silvio void CMyListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC dc; CBrush brush; brush.CreateSolidBrush( RGB(255, 255, 0) ); CRect rc; rc = lpDrawItemStruct->rcItem; CString csItemText; GetItemText( lpDrawItemStruct->itemID, 0, csItemText.GetBuffer(255), 255); csItemText.ReleaseBuffer( -1 ); COLORREF textColor; if( lpDrawItemStruct->itemState & ODS_SELECTED ) textColor = g_MyLookAndFeel.m_ListItemSecondaryForegroundColor; else textColor = g_MyLookAndFeel.m_ListItemPrimaryForegroundColor; ::SetTextColor( lpDrawItemStruct->hDC, textColor ); ::SetBkMode( lpDrawItemStruct->hDC, TRANSPARENT ); ::DrawText( lpDrawItemStruct->hDC, csItemText.GetBuffer(0), csItemText.GetLength(), &rc, DT_LEFT ); } void CMyListCtrl::OnPaint() { TRACE("CMyListCtrl::OnPaint()\n"); if( ! (GetStyle() & LVS_REPORT) ) CListCtrl::OnPaint(); else { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here // Used later to determine which items to draw (not all are visible) int nFirstVisibleItemIndex = -1; // Finds the first visible item in the list (in case the list has been scrolled) // We use it to figure out where to place the origin of our grid and to obtain the height of each row CRect rcFirstVisibleItem( -1, -1, -1, -1 ); for(int nItemIndex = 0; nItemIndex < this->GetItemCount(); nItemIndex++) { GetItemRect( nItemIndex, &rcFirstVisibleItem, LVIR_BOUNDS ); if( rcFirstVisibleItem.top>0) { nFirstVisibleItemIndex = nItemIndex; break; } } // Obtains the rect of the list view window in client coords CRect rcWindowRect; int nVisibleRowCount; GetWindowRect ( &rcWindowRect ); rcWindowRect -= rcWindowRect.TopLeft(); nVisibleRowCount = rcWindowRect.Height() / rcFirstVisibleItem.Height(); // Now we draw each column with the grid effect CRect rcItemRect; LVCOLUMN lvColumn; lvColumn.mask = LVCF_WIDTH; rcItemRect.left = rcFirstVisibleItem.left; for( int nColumn = 0; GetColumn( nColumn, &lvColumn ); nColumn++ ) { // Initializes cell rect rcItemRect.top = rcFirstVisibleItem.top; rcItemRect.right = rcItemRect.left + lvColumn.cx; rcItemRect.bottom = rcItemRect.top + rcFirstVisibleItem.Height(); for( int nRow = 0; nRow < nVisibleRowCount; nRow++ ) { CRect rcCellRect( &rcItemRect ); // If cell rect falls outside the window rect, then we need not go through the other rows. if( rcItemRect.top>= rcWindowRect.bottom ) break; rcCellRect.DeflateRect(0, 0, 1, 1); // Draws cell rect COLORREF cellColor; if( GetItemState( nRow, LVIS_SELECTED ) ) cellColor = g_MyLookAndFeel.m_ListItemSecondaryBackgroundColor; else cellColor = g_MyLookAndFeel.m_ListItemPrimaryBackgroundColor; dc.FillSolidRect( &rcCellRect, cellColor ); // Adjusts rect for the next row rcItemRect.OffsetRect(0, rcItemRect.Height() ); } // Adjusts rect for the next column rcItemRect.OffsetRect( lvColumn.cx, 0 ); } // Now we must call DrawItem() for each visible item! // Finds the first visible item in the list (in case the list has been scrolled) // We use it to figure out where to place the origin of our grid and to obtain the height of each row for( nItemIndex = nFirstVisibleItemIndex; nItemIndex < this->GetItemCount(); nItemIndex++ ) { DRAWITEMSTRUCT drawItemStruct; CRect rcItemRect; UINT itemState; GetItemRect( nItemIndex, &rcItemRect, LVIR_BOUNDS ); // Break if item is past the bottom of the window if( rcItemRect.top>= rcWindowRect.bottom ) break; itemState = GetItemState( nItemIndex, -1 ); drawItemStruct.itemState = 0; drawItemStruct.itemState |= ( itemState & LVIS_SELECTED ? ODS_SELECTED : 0 ); drawItemStruct.itemState |= ( itemState & LVIS_FOCUSED ? ODS_FOCUS : 0 ); drawItemStruct.CtlType = ODT_LISTVIEW; drawItemStruct.CtlID = GetDlgCtrlID(); drawItemStruct.itemID = nItemIndex; drawItemStruct.itemAction = ODA_DRAWENTIRE; drawItemStruct.hwndItem = GetSafeHwnd( ); drawItemStruct.hDC = dc.GetSafeHdc( ); drawItemStruct.rcItem = rcItemRect; DrawItem( &drawItemStruct ); TRACE("Drawing item %d style: %d", nItemIndex, drawItemStruct.itemState); TRACE("\n"); } } } - |
