Simplified Subitem Editing
Posted
by Gunnar Andre Dalsnes
on January 3rd, 2003

Click here for a larger image.
Environment: VC6 SP5
This article suggests a way to subclass the listviews edit control, but unlike the article "Editing listview Subitems Using LVM_GETEDITCONTROL," it uses a much simpler and hopefully better method. Give it a try!
- Vars
public: CSubEdit m_editWnd; int m_item; int m_subitem; CSubeditListView::CSubeditListView() { m_subitem = 0; } - Detect subitem mouse clicks
void CSubeditListView::OnLButtonDown(UINT nFlags, CPoint point) { LVHITTESTINFO lvhit; lvhit.pt = point; int item = GetListCtrl().SubItemHitTest(&lvhit); //if (over a subitem) if (item != -1 && lvhit.iSubItem && (lvhit.flags & LVHT_ONITEM )) { //mouse click outside the editbox in an already //editing cell cancels editing if (m_subitem == lvhit.iSubItem && item == m_item) { CListView::OnLButtonDown(nFlags, point); } else { CListView::OnLButtonDown(nFlags, point); m_subitem = lvhit.iSubItem; m_item = item; GetListCtrl().EditLabel(item); } } else CListView::OnLButtonDown(nFlags, point); } - Handle OnBeginLabelEdit
void CSubeditListView::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; //if (subitem selected for editing) if (m_subitem) { ASSERT(m_item == pDispInfo->item.iItem); CRect subrect; GetListCtrl().GetSubItemRect( pDispInfo->item.iItem, m_subitem, LVIR_BOUNDS , subrect ); //get edit control and subclass HWND hWnd=(HWND)SendMessage(LVM_GETEDITCONTROL); ASSERT(hWnd!=NULL); VERIFY(m_editWnd.SubclassWindow(hWnd)); //move edit control text 1 pixel to the right of org label, //as Windows does it... m_editWnd.m_x=subrect.left + 6; m_editWnd.SetWindowText(GetListCtrl().GetItemText (pDispInfo->item.iItem,m_subitem)); //hide subitem text so it don't show if we delete some //text in the edit control //OnPaint handles other issues also regarding this CRect rect; GetListCtrl().GetSubItemRect(pDispInfo->item.iItem,m_subitem, LVIR_LABEL ,rect); CDC* hDc = GetDC(); hDc->FillRect(rect,&CBrush(::GetSysColor(COLOR_WINDOW))); ReleaseDC(hDc); } //return: editing permitted *pResult = 0; } - Positioning the editcontrol
void CSubEdit::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) { lpwndpos->x=m_x; CEdit::OnWindowPosChanging(lpwndpos); } - Handle painting
void CSubeditListView::OnPaint() { //if (subitem editing) if (m_subitem) { CRect rect; CRect editrect; GetListCtrl().GetSubItemRect(m_item,m_subitem,LVIR_LABEL, rect); m_editWnd.GetWindowRect(editrect); ScreenToClient(editrect); //block text redraw of the subitems text (underneath the //editcontrol) //if we didn't do this and deleted some text in the edit //control, the subitems original label would show if (editrect.right < rect.right) { rect.left = editrect.right ; ValidateRect(rect); } //block filling redraw of leftmost item (caused by FillRect) GetListCtrl().GetItemRect(m_item,rect,LVIR_LABEL ); ValidateRect(rect); } CListView::OnPaint(); } - Handle resizing
void CSubeditListView::OnSize(UINT nType, int cx, int cy) { //stop editing if resizing if( GetFocus() != this ) SetFocus(); CListView::OnSize(nType, cx, cy); } - Handle OnEndLabelEdit
void CSubeditListView::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR; LV_ITEM *plvItem = &plvDispInfo->item; //if (end of sub-editing) do cleanup if (m_subitem) { //plvItem->pszText is NULL if editing canceled if (plvItem->pszText != NULL ) GetListCtrl().SetItemText(plvItem->iItem,m_subitem, plvItem->pszText); VERIFY(m_editWnd.UnsubclassWindow()!=NULL); m_subitem = 0; //always revert to org label (Windows thinks we are editing //the leftmost item) *pResult = 0; } else //return: update label on leftmost item *pResult = 1; }
Downloads
Download exe - 10 KbDownload source - 40 Kb

Comments
CDialog
Posted by break; on 08/04/2006 04:13amGreat stuff, but how to use it in an dialog application with CListCtrl? In my case the message "OnLButtonDown" dont take an efect for my CListCtrl, only for my dialog! thanks break;
ReplyBetter edit box creation timing
Posted by swedishbricks on 02/16/2006 05:19pmIf you replace the handler for WM_LBUTTONDOWN with a handler for NM_CLICK that looks like this: LRESULT CEditListView::OnClick(LPNMHDR nmhdr) { LPNMITEMACTIVATE nmia = \ reinterpret_cast(nmhdr);
LVHITTESTINFO lvhit;
lvhit.pt = nmia->ptAction;
int HitItem = SubItemHitTest(&lvhit);
// If the click was on a subitem text of a selected
// item and we are not already editing something
if (HitItem != -1 && lvhit.iSubItem > 0 &&
(lvhit.flags & LVHT_ONITEMLABEL) != 0 &&
GetSelectedCount() > 0 &&
GetItemState(HitItem, LVIS_SELECTED) != 0 &&
GetEditControl() == NULL)
{
// Remember the subitem that we are working on here
m_Item = HitItem;
m_Subitem = lvhit.iSubItem;
}
// Let the default handling do it's thing
SetMsgHandled(FALSE);
// NM_CLICK doesn't care about the return value
return 0;
}
... you will get the edit box displayed with the same delayed timing for subitems as for the main item.
Cheers,
-- Matt ReplyProblem using CListCtrl
Posted by Legacy on 12/16/2003 12:00amOriginally posted by: Shashi Prabhakar
-
-
ReplyProblem using CListCtrl
Posted by imbtf on 06/27/2004 09:50amMake sure "CPaintDC dc(this);" is NOT present in your OnPaint(); Also, at the end of OnPaint(), immedtiately before it returns call "DefWindowProc(WM_PAINT, NULL, NULL);" This will remove the problem of the first item not being displayed when editing subitems.
ReplyCListCtrl
Posted by hankdane on 06/01/2004 07:32pmI used the control with a CListCtrl, and it works just fine. I just removed all calls to GetListCtrl(). I also see the text disappearence problem. If I find a solution, I will post it here.
Replybeginner's question
Posted by Legacy on 11/10/2003 12:00amOriginally posted by: gatsby
i need help, guys:
i need to use the editable item and subitem on a dialog based application. what should i do?
thanks.
-
ReplyUse the Control
Posted by hankdane on 06/01/2004 07:35pmYou can use Gunnar's control from a dialog based application. I did.
ReplyBug plus solution
Posted by Legacy on 07/17/2003 12:00amOriginally posted by: Sander
Hi,
Great stuff. Been having one problem, though.
When the view is resized or populated so that a vertical scroll bar is necessary, a problem occurs when exiting the application.
This only happens in debug mode and the call stack at the particular point is very unclear, as "our list view class" is nowhere in it.
Though, it seems to have to do with the OnSize implementation of this solution.
I think that this:
if( GetFocus() != this ) SetFocus();
...should be something like this:
if( "in edit mode" )
if( GetFocus() != this ) SetFocus();
However, if you look at how Windows does this normally for you (thus when editing the main item, not a sub item) just leaving this out immitates best the normal behaviour. Ergo; I removed the OnSize method and am happy again.
Good luck with it.
Greetings,
Sander
Reply
Urgent Help Needed
Posted by Legacy on 07/09/2003 12:00amOriginally posted by: Gary
Hi,
The list control is wonderful. But, I have one problem. Just as we have CBN_SELCHANGE message in combobox to trap the change of selection event, what event shall I use in this list control? Basically, I want to display related records in a different grid when the selection in list control changes. I could not find the event to do this.
Please help me.
Thanks,
- Gary
Reply
OnLButtonDown?
Posted by Legacy on 06/02/2003 12:00amOriginally posted by: real name
it looks different to system logic (and btw breaks dnd)
t!
Replyhorizontal scrolling - tip
Posted by Legacy on 03/12/2003 12:00amOriginally posted by: marcus gesing
ReplyASSERT failure
Posted by Legacy on 02/07/2003 12:00amOriginally posted by: Luke Johnston
When a lot of items are added, say 100, I get an ASSERT failure in wincore.cpp upon exiting the program.
Any ideas?
ReplyThanks and bug
Posted by Legacy on 01/28/2003 12:00amOriginally posted by: Sylvain PEYRUQUEOU
Thank you very much for your code, it was exactly what I need.
However, I observe a bug when I click on a cell which is being edited. Is there anybody who has the same ?
ReplyLoading, Please Wait ...