dcsimg
December 4, 2016
Hot Topics:

List control with single / double separator lines

  • October 5, 1998
  • By Zoran M. Todorovic
  • Send Email »
  • More Articles »

This article describes how to add separator lines (single line or double line) to a list control derived from CListCtrl. This technique requires that the list control is owner drawn. This article also demonstrates how to prevent row selection in a list control.

grid_separator.gif (2590 bytes)

How to do it?

Let's assume that we have a class TVisualListCtrl derived from CListCtrl. Objects of this class correspond to list control resources (each list control must be ownerdrawn). In order to implement support for separator lines, we have to add several features to this class.

Step 1.

Add a public enum type containing the separator line identifiers and 2 public functions for inserting a separator line and checking whether the item is separator or not.


class TVisualCtrlList : public
CListCtrl {
public:
    enum ESeparator {
        SEPARATOR_SINGLE,
            SEPARATOR_DOUBLE
    };
    ......
public:
    .....
    BOOL IsSeparator(int row);
    int InsertSeparator(int row, ESeparator separator = SEPARATOR_SINGLE);
};

Also create 2 constant strings (within a class or as local variables in a .cpp implementation file) which are inserted into the list control. These strings have a special meaning to the class implementation since they are actually aliases to separator lines.



const CString SingleSeparator = _T("---");

const CString DoubleSeparator = _T("===");

Now, the implementation of the above 2 public functions become the following:



BOOL TVisualListCtrl::IsSeparator(int index)

{

    CString label = GetItemText(index,0);

    if ((label == SingleSeparator) || (label == DoubleSeparator))

        return TRUE;

    return FALSE;

}



int TVisualListCtrl::InsertSeparator(int index, ESeparator separator)

{

    switch (separator) {

    case SEPARATOR_SINGLE: return InsertItem(index,SingleSeparator);

    case SEPARATOR_DOUBLE: return InsertItem(index,DoubleSeparator);

    }

    return -1;

}

This step finalizes the interface of the TVisualListCtrl class regarding the separator lines. Now, let's see how to implement the inner functionality.

Step 2.

Every list control derived class (for ownerdrawn list controls) has a member function:



void TVisualListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

This function is called each time an item in a list control needs to be drawn. This is were major modifications has to be performed. First, based on the argument, we have to find out the row number of the item being drawn, then we have to get the label of the item and check whether it is a separator or not.



int nItem = lpDrawItemStruct->itemID;

CString sLabel = GetItemText(nItem, 0);

BOOL bSeparator = IsSeparator(nItem);

So far so good. Now, if the item is not a separator, we have to draw it. This is discussed in details in several articles on the Codeguru site. However, if the item is a separator (bSeparator is TRUE), then we have to draw it (instead of drawing the item). Here is the code for drawing the separator.



CClientDC dc(this);

CRect rect;

GetClientRect(&rect);

int height = rcItem.bottom - rcItem.top;

height >>= 1;

CPen *oldpen, graypen(PS_SOLID,1,RGB(192,192,192));

CPen blackpen(PS_SOLID,1,RGB(0,0,0));

if (sLabel == SingleSeparator) {

    oldpen = dc.SelectObject(&blackpen);

    dc.MoveTo(rect.left,rcItem.top + height+1);

    dc.LineTo(rect.right,rcItem.top + height+1);

    dc.SelectObject(&graypen);

    dc.MoveTo(rect.left,rcItem.top + height);

    dc.LineTo(rect.right,rcItem.top + height);

} else {

    oldpen = dc.SelectObject(&blackpen);

    dc.MoveTo(rect.left,rcItem.top + height+2);

    dc.LineTo(rect.right,rcItem.top + height+2);

    dc.MoveTo(rect.left,rcItem.top + height-1);

    dc.LineTo(rect.right,rcItem.top + height-1);

    dc.SelectObject(&graypen);

    dc.MoveTo(rect.left,rcItem.top + height+1);

    dc.LineTo(rect.right,rcItem.top + height+1);

    dc.MoveTo(rect.left,rcItem.top + height-2);

    dc.LineTo(rect.right,rcItem.top + height-2);

}

dc.SelectObject(oldpen);

Be sure not to draw the highlight rectangle if the item to draw is a separator. This is important and will be expalined in the next step.

Step 3.

In the final step, we have to prevent the user to select the separator. In order to do this, we have to override the following messages sent to the list control.

  • WM_KEYDOWN
  • WM_KEYUP
  • WM_LBUTONDOWN

Handle for WM_KEYDOWN handles the selection when the user pressed a character (default list control behaviour is to find the item whose label begins with the typed character). Here is the implementation:



void TVisualListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 

{

    if ((nChar != SingleSeparator[0]) && (nChar != DoubleSeparator[0]))

        CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);

}

Handle for WM_KEYUP handles the cursor movements (up, down, page up etc.). We have to prevent the user to select the separator and select previous or next item instead. Here is the implementation:



void TVisualListCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 

 {

     CListCtrl::OnKeyUp(nChar, nRepCnt, nFlags);



     int index = GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);

     int newindex = index;

     if (IsSeparator(index)) {

         switch (nChar) {

         case VK_DOWN:

         case VK_END:

         case VK_NEXT:

             while (newindex < GetItemCount()-1) {

                 newindex++;

                 if (!IsSeparator(newindex))

                     break;

             };

             if (IsSeparator(newindex)) {

                 newindex = index;

                 while

                     (newindex > 0) {

                     newindex--;

                     if (!IsSeparator(newindex))

                         break;

                 };

             }

             SetCurSel(newindex);

             EnsureVisible(newindex,TRUE);

             break;

         case VK_UP:

         case VK_HOME:

         case VK_PRIOR:

             while (newindex > 0)

             {

                 newindex--;

                 if (!IsSeparator(newindex))

                     break;

             };

             if (IsSeparator(newindex)) {

                 newindex = index;

                 while

                     (newindex < GetItemCount()-1) {

                     newindex++;

                     if (!IsSeparator(newindex))

                         break;

                 };

             }

             SetCurSel(newindex);

             EnsureVisible(newindex,TRUE);

             break;

         };

     }

 }

Handle for WM_LBUTTONDOWN handles the selection via the mouse (click with the left mouse button). In this case, we have to prevent the user to select the separator (default handler is executed only if mouse is clicked on item other then separator). Here is the implementation:



void TVisualListCtrl::OnLButtonDown(UINT nFlags, CPoint point) 

 {

     CPoint pt = point;

     pt.x = 3;

     int index = HitTest(pt);

     if ((index != -1) && IsSeparator(index))

         return;



     CListCtrl::OnLButtonDown(nFlags, point);

 }

Conclusion

Separator line in a list control offers a lot of possibilities since it is a visual separator between different sections of items. The only drawback is the height of the separator item - it has the same height as the regular item.

Full source code

Here is the complete source code of the TVisualListCtrl. This code is partly based on articles from the great Codeguru site. I am listing all the code here for convenience.

Include file:



class AFX_EXT_CLASS TVisualListCtrl : public CListCtrl {

public: 

    enum ESeparator {

        SEPARATOR_SINGLE,

        SEPARATOR_DOUBLE

    };

    enum EHighlight {

        HIGHLIGHT_NORMAL, 

        HIGHLIGHT_ALLCOLUMNS, 

        HIGHLIGHT_ROW

    };

    

protected:

    int HighlightType; // One of EHighligh enums

    BOOL VerticalGrid;

    BOOL HorizontalGrid;

    COLORREF ForegroundColor;

    

public:

    TVisualListCtrl();

    virtual ~TVisualListCtrl();

    

    BOOL SetCurSel(int row);

    void RepaintSelectedItems();

    int SetHighlightType(EHighlight hilite);

    void SetForegroundColor(COLORREF foreColor) { ForegroundColor = foreColor; }

    void DrawVerticalGrid(BOOL onoff) { VerticalGrid = onoff; }

    void DrawHorizontalGrid(BOOL onoff)  { HorizontalGrid = onoff; }

    BOOL IsSeparator(int index);

    int InsertSeparator(int index, ESeparator separator);

    

public:

    //{{AFX_VIRTUAL(TVisualListCtrl)

protected:

    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

    virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);

    //}}AFX_VIRTUAL

    

protected:

    //{{AFX_MSG(TVisualListCtrl)

    afx_msg void OnPaint();

    afx_msg void OnKillFocus(CWnd* pNewWnd);

    afx_msg void OnSetFocus(CWnd* pOldWnd);

    afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);

    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);

    //}}AFX_MSG

    

    DECLARE_MESSAGE_MAP()

 };

Source code:



const CString SingleSeparator(_T('-'),3);

const CString DoubleSeparator(_T('='),3);



BEGIN_MESSAGE_MAP(TVisualListCtrl, CListCtrl)

//{{AFX_MSG_MAP(TVisualListCtrl)

ON_WM_PAINT()

ON_WM_KILLFOCUS()

ON_WM_SETFOCUS()

ON_WM_KEYUP()

ON_WM_LBUTTONDOWN()

ON_WM_KEYDOWN()

//}}AFX_MSG_MAP

END_MESSAGE_MAP()



TVisualListCtrl::TVisualListCtrl()

{

    HighlightType = HIGHLIGHT_ALLCOLUMNS;

    VerticalGrid = FALSE;

    HorizontalGrid = FALSE;

    ForegroundColor = 0;

}



TVisualListCtrl::~TVisualListCtrl()

{

}



BOOL TVisualListCtrl::IsSeparator(int index)

{

    CString label = GetItemText(index,0);

    if ((label == SingleSeparator) || (label == DoubleSeparator))

        return TRUE;

    return FALSE;

}



int TVisualListCtrl::InsertSeparator(int index, ESeparator separator)

{

    switch (separator) {

    case SEPARATOR_SINGLE: return InsertItem(index,SingleSeparator);

    case SEPARATOR_DOUBLE: return InsertItem(index,DoubleSeparator);

    }

    return -1;

}



// Unless LVS_SHOWSELALWAYS is set for list control, new selection is visible 

// only when the list control receives a focus

BOOL TVisualListCtrl::SetCurSel(int row)

{

    return SetItemState(row,

        LVIS_SELECTED|LVIS_FOCUSED,LVIS_SELECTED|LVIS_FOCUSED);

}



void TVisualListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

{

    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);

    CRect rcItem(lpDrawItemStruct->rcItem); 

    int nItem = lpDrawItemStruct->itemID;

    CImageList* pImageList; 

    // Save dc state 

    int nSavedDC = pDC->SaveDC();

    

    // Get item image and state info 

    LV_ITEM lvi;

    lvi.mask = LVIF_IMAGE | LVIF_STATE; 

    lvi.iItem = nItem; 

    lvi.iSubItem = 0;

    lvi.stateMask = 0xFFFF; 

    // get all state flags 

    GetItem(&lvi);

    

    // Should the item be highlighted

    BOOL bHighlight =

        ((lvi.state & LVIS_DROPHILITED) ||

        ((lvi.state & LVIS_SELECTED)

        && ((GetFocus()

        == this) || (GetStyle() & LVS_SHOWSELALWAYS))));

    

    // Get rectangles for drawing 

    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);

    BOOL bSeparator = IsSeparator(nItem);

    // Labels are offset by a certain amount 

    // This offset is related to the width of a space character

    int offset = pDC->GetTextExtent(_T(" "), 1).cx*2;

    

    

    CRect rcHighlight;

    CRect rcWnd; 

    int nExt; 

    

    switch (HighlightType) { 

    case HIGHLIGHT_NORMAL: 

        nExt = pDC->GetOutputTextExtent(sLabel).cx + offset; 

        rcHighlight = rcLabel;

        if( rcLabel.left + nExt < rcLabel.right )

            rcHighlight.right = rcLabel.left + nExt; 

        break; 

    case HIGHLIGHT_ALLCOLUMNS:

        rcHighlight = rcBounds;

        rcHighlight.left = rcLabel.left;

        break; 

    case HIGHLIGHT_ROW:

        GetClientRect(&rcWnd);

        rcHighlight = rcBounds;

        rcHighlight.left = rcLabel.left;

        rcHighlight.right = rcWnd.right;

        

        break;

    default: 

        rcHighlight = rcLabel; 

    } 

    

    // Draw the background color

    if (bHighlight) {

        if (!bSeparator) {

            pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));

            pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));

            pDC->FillRect(rcHighlight,

                &CBrush(::GetSysColor(COLOR_HIGHLIGHT))); 

        }

    } else {

        pDC->FillRect(rcHighlight,

            &CBrush(::GetSysColor(COLOR_WINDOW))); 

        if (ForegroundColor != 0)

            pDC->SetTextColor(ForegroundColor);

    }

    

    // Set clip region 

    rcCol.right = rcCol.left + GetColumnWidth(0); 

    CRgn rgn;

    rgn.CreateRectRgnIndirect(&rcCol); 

    pDC->SelectClipRgn(&rgn);

    rgn.DeleteObject(); 

    

    if (!bSeparator) {

        // Draw state icon 

        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);

                

            } 

        } 

        // Draw normal and overlay icon

        

        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 ); 

        }

        // Draw item label - Column 0

        

        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); 

        // Draw labels for remaining columns

        LV_COLUMN lvc; 

        lvc.mask = LVCF_FMT | LVCF_WIDTH;

        if (HighlightType == 0) {

            // Highlight only first column

            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;

            // Draw the background if needed 

            if (HighlightType == HIGHLIGHT_NORMAL )

                pDC->FillRect(rcCol,

                &CBrush(::GetSysColor(COLOR_WINDOW)));

            sLabel = GetItemText(nItem, nColumn); 

            if (sLabel.GetLength() == 0)

                continue;

            

            // Get the text justification 

            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;

            }

            

            rcLabel = rcCol;

            

            rcLabel.left += offset;

            rcLabel.right -= offset;

            pDC->DrawText(sLabel,

                -1, rcLabel, nJustify | DT_SINGLELINE | 

                DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS); 

        }

        // Draw focus rectangle if item has focus

        if (lvi.state & LVIS_FOCUSED && (GetFocus() == this))

            pDC->DrawFocusRect(rcHighlight);

    } else {

        CClientDC dc(this);

        CRect rect;

        GetClientRect(&rect);

        int height = rcItem.bottom - rcItem.top;

        height >>= 1;

        CPen *oldpen, graypen(PS_SOLID,1,RGB(192,192,192));

        CPen blackpen(PS_SOLID,1,RGB(0,0,0));

        if (sLabel == SingleSeparator) {

            oldpen = dc.SelectObject(&blackpen);

            dc.MoveTo(rect.left,rcItem.top + height+1);

            dc.LineTo(rect.right,rcItem.top + height+1);

            dc.SelectObject(&graypen);

            dc.MoveTo(rect.left,rcItem.top + height);

            dc.LineTo(rect.right,rcItem.top + height);

        } else {

            oldpen = dc.SelectObject(&blackpen);

            dc.MoveTo(rect.left,rcItem.top + height+2);

            dc.LineTo(rect.right,rcItem.top + height+2);

            dc.MoveTo(rect.left,rcItem.top + height-1);

            dc.LineTo(rect.right,rcItem.top + height-1);

            dc.SelectObject(&graypen);

            dc.MoveTo(rect.left,rcItem.top + height+1);

            dc.LineTo(rect.right,rcItem.top + height+1);

            dc.MoveTo(rect.left,rcItem.top + height-2);

            dc.LineTo(rect.right,rcItem.top + height-2);

        }

        dc.SelectObject(oldpen);

    }

    // Restore dc 

    pDC->RestoreDC( nSavedDC );

}



void TVisualListCtrl::RepaintSelectedItems()

{

    CRect rcBounds, rcLabel;

    

    // Invalidate focused item so it can repaint 

    int nItem = GetNextItem(-1, LVNI_FOCUSED);

    

    if (nItem != -1) {

        GetItemRect(nItem, rcBounds, LVIR_BOUNDS);

        GetItemRect(nItem, rcLabel, LVIR_LABEL);

        rcBounds.left = rcLabel.left;

        InvalidateRect(rcBounds, FALSE);

    }

    

    // Invalidate selected items depending on LVS_SHOWSELALWAYS

    if (!(GetStyle() & LVS_SHOWSELALWAYS)) {

        for (nItem = GetNextItem(-1, LVNI_SELECTED); nItem != -1; 

        nItem = GetNextItem(nItem, LVNI_SELECTED))

        {

            GetItemRect(nItem, rcBounds, LVIR_BOUNDS);

            GetItemRect(nItem, rcLabel, LVIR_LABEL);

            rcBounds.left = rcLabel.left;

            InvalidateRect(rcBounds, FALSE);

        }

    }

    UpdateWindow();

}



void TVisualListCtrl::OnPaint() 

{

    CListCtrl::OnPaint();

    // in full row select mode, we need to extend the clipping region

    // so we can paint a selection all the way to the right

    if (HighlightType == HIGHLIGHT_ROW && (GetStyle() &

        LVS_TYPEMASK) == LVS_REPORT ) {

        CRect rcBounds;

        GetItemRect(0, rcBounds, LVIR_BOUNDS);

        

        CRect rcClient;

        GetClientRect(&rcClient);

        

        if (rcBounds.right < rcClient.right) {

            CPaintDC dc(this);

            CRect rcClip;

            

            dc.GetClipBox(rcClip);

            rcClip.left = min(rcBounds.right-1, rcClip.left);

            rcClip.right = rcClient.right; 

            InvalidateRect(rcClip, FALSE); 

        } 

    }

    // Paint vertical and horizontal lines

    // Draw the lines only for LVS_REPORT mode

    if( (GetStyle() & LVS_TYPEMASK) == LVS_REPORT ) {

        

        // Get the number of columns

        CClientDC dc(this );

        CPen pen(PS_SOLID,1,RGB(192,192,192));

        CPen *oldpen = dc.SelectObject(&pen);

        RECT rect;

        CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);

        int nColumnCount = pHeader->GetItemCount();

        // The bottom of the header corresponds to the top of the line 

        pHeader->GetClientRect(&rect);

        int top = rect.bottom;

        // Now get the client rect so we know the line length and when to stop

        GetClientRect(&rect);

        if (VerticalGrid) {

            // The border of the column is offset by the horz scroll

            int borderx = 0 - GetScrollPos(SB_HORZ);

            for (int i = 0; i < nColumnCount; i++) {

                // Get the next border

                borderx += GetColumnWidth(i);

                //if next border is outside client area, break out

                if ( borderx >= rect.right ) 

                    break;

                

                // Draw the line

                dc.MoveTo(borderx-1,top);

                dc.LineTo(borderx-1,

                    rect.bottom );

            }

        }

        // Draw the horizontal grid lines

        

        if (HorizontalGrid) {

            // First get the height

            if (GetItemRect( 0,

                &rect, LVIR_BOUNDS)) {

                int height = rect.bottom - rect.top; 

                GetClientRect(&rect);

                int width = rect.right; 

                for (int i = 1; i <= GetCountPerPage(); i++) {

                    dc.MoveTo(0, top + height*i);

                    dc.LineTo(width, top + height*i);

                }

            }

        }

        dc.SelectObject(oldpen);

    }

    //CPaintDC dc(this); // device context for painting

}



void TVisualListCtrl::OnKillFocus(CWnd* pNewWnd) 

{

    CListCtrl::OnKillFocus(pNewWnd);

    // check if we are losing focus to label edit box

    if (pNewWnd != NULL && pNewWnd->GetParent() == this)

        return;

    // repaint items that should change appearance

    if ((GetStyle() & LVS_TYPEMASK) == LVS_REPORT)

        RepaintSelectedItems();

}



void TVisualListCtrl::OnSetFocus(CWnd* pOldWnd) 

{

    CListCtrl::OnSetFocus(pOldWnd);

    // check if we are getting focus from label edit box

    if (pOldWnd!=NULL && pOldWnd->GetParent()==this)

        return;

    // repaint items that should change appearance

    if ((GetStyle() & LVS_TYPEMASK)==LVS_REPORT)

        RepaintSelectedItems();

}



int TVisualListCtrl::SetHighlightType(EHighlight hilite)

{

    int oldhilite = HighlightType;

    if (hilite <= HIGHLIGHT_ROW ) {

        HighlightType = hilite;

        Invalidate(); 

    } 

    return oldhilite;

}



BOOL TVisualListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 

{

    HD_NOTIFY *pHD = (HD_NOTIFY*)lParam;

    if ((pHD->hdr.code == HDN_ITEMCHANGINGA || pHD->hdr.code ==

        HDN_ITEMCHANGINGW) &&

        (GetStyle() & LVS_TYPEMASK) == LVS_REPORT)

    { 

        // Invalidate empty bottom part of control to force erase the previous 

        // position of column

        int nBottom, nLastItem = GetItemCount()-1;

        if (nLastItem < 0) {

            // List is empty : invalidate whole client rect

            nBottom = 0;

        } else { 

            // Get Y position of bottom of list (last item)

            RECT ItemRect;

            GetItemRect(nLastItem,&ItemRect,LVIR_BOUNDS);

            nBottom = ItemRect.bottom;

        }

        RECT rect;

        GetClientRect(&rect);

        if (nBottom < rect.bottom) { 

            // Set top of rect as bottom of list (last item) : rect = empty part of list

            rect.top = nBottom;

            InvalidateRect(&rect);

        }

        // NB: We must go on with default processing. 

    } 

    *pResult = 0;

    return CListCtrl::OnNotify(wParam, lParam, pResult);

}



void TVisualListCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 

{

    if ((nChar != SingleSeparator[0]) && (nChar != DoubleSeparator[0]))

        CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);

}



void TVisualListCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 

{

    CListCtrl::OnKeyUp(nChar, nRepCnt, nFlags);

    int index = GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);

    int newindex = index;

    if (IsSeparator(index)) {

        switch (nChar) {

        case VK_DOWN:

        case VK_END:

        case VK_NEXT:

            while (newindex < GetItemCount()-1) {

                newindex++;

                if (!IsSeparator(newindex))

                    break;

            };

            if (IsSeparator(newindex)) {

                newindex = index;

                while (newindex > 0) {

                    newindex--;

                    if (!IsSeparator(newindex))

                        break;

                };

            }

            SetCurSel(newindex);

            EnsureVisible(newindex,TRUE);

            break;

        case VK_UP:

        case VK_HOME:

        case VK_PRIOR:

            while (newindex > 0)

            {

                newindex--;

                if (!IsSeparator(newindex))

                    break;

            };

            if (IsSeparator(newindex)) {

                newindex = index;

                while (newindex < GetItemCount()-1) {

                    newindex++;

                    if (!IsSeparator(newindex))

                        break;

                };

            }

            SetCurSel(newindex);

            EnsureVisible(newindex,TRUE);

            break;

        };

    }

}



void TVisualListCtrl::OnLButtonDown(UINT nFlags, CPoint point) 

{

    CPoint pt = point;

    pt.x = 3;

    int index = HitTest(pt);

    if ((index != -1) && IsSeparator(index))

        return;

    CListCtrl::OnLButtonDown(nFlags, point);

}

Hope you find this useful !

Date Posted: 5th October 1998






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel