Microsoft & .NETVisual C#List control with single / double separator lines

List control with single / double separator lines

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories