Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / MFC

Local Input With No Effort

Rate me:
Please Sign up or sign in to vote.
4.84/5 (45 votes)
16 Oct 2009CPOL5 min read 162.7K   4.1K   66   46
A sort of "add-on" to any application, which does not change at all the behaviour of the application but solves the "local edit" problem in a unitary way.
Sample Image

Introduction

A lot of approaches have been made in order to improve MFC classes like CListCtrl (report - style), CListBox, or even CTreeCtrl (even though the last one allows for label editing), in the sense of allowing the user to edit their items "in-line". Those approaches are very elaborate, and require a lot of care to be taken when coming to treating as many of their events as possible in order to prevent unwanted behaviours. When trying to improve older applications, a user has to replace the declarations of his / her objects with new ones related to the provided derived classes, and should test the applications extensively against possible unwanted "features".

What if a class - a single one - would allow the modification, on an item base, of all the CListCtrl, CListBox, CTreeCtrl, and even the CComboBox (after all, the last class makes use of CListBox) objects of an application without changing the object declarations and disregarding the style combinations that may apply to some or all of the objects?

Note: After the updates dated March 23, the content of the list box used by the CComboBox control is visible while doing the local input.

combo image

The LocalInputDlg comes as a sort of "add-on" to any application, it does not change the behaviour of the application at all but solves the "local edit" problem in a unitary way. Try this class and you will add only two lines of code to your application. In return, a right-click on items belonging to objects of the above mentioned type will offer you the possibility to locally edit them.

Using the LocalInputDlg

Here are the steps you have to follow to add the "local edit" functionality to your beloved application:

  1. Add LocalInputDlg.h and LocalInputDlg.cpp files to your project.
  2. Uncomment the LIWNE_USE_LVN_ENDLABELEDIT in LocalInputDlg.h if you handle the LVN_ENDLABELEDIT notification in your application.
  3. Include LocalInputDlg.h in YourApp.h.
  4. Use the wizard to add the PreTranslateMessage() virtual function to YourApp.cpp.
    1. Add the LocalInputDlg dlgInput member to YourApp class.
    2. Inside the generated function, write the following line of code, exactly after the TODO remark line:
      C++
      if (dlgInput.DoLocalInput(pMsg, true)) return TRUE;

      should the chosen style be "arrow", or:

      C++
      if (dlgInput.DoLocalInput(pMsg, false)) return TRUE;

      for the "in-line" style.

That's all! Compile your project and here you are!

The Things Behind

In its OnInitDialog() function, LocalInputDlg positions itself depending on the position of the clicked item, and positions and resizes its controls accordingly. The width of the input field reflects the current width of the item. The window region is set so that the dialog gets either the "arrow" style or the "in-line edit" style aspect.

The local edit process starts by calling the DoLocalInput() with a MESSAGE pointer, provided by the PreTranslateMessage() function of the application, as parameter. The DoLocalInput() function checks if it deals with a WM_RBUTTONDOWN (other messages can be used in place). In the case of this message, the function decides the way the message is to be processed. The pMsg->hwnd handle is used to get a pointer to the window posting the message, which is type-cast to a CListBox* pointer. Let this pointer be pLB. With pLB->GetCount() returning a positive number, the function decides it is dealing with a CListBox object (this is also the case of the CComboBox, as the message is coming from a temporary window which, in fact, is a CListBox). Otherwise, the function gets the run-time class of the window posting the message (in the previous decision making, this was not working, as the window was temporary). Should the run-time class be a CTreeCtrl, the function deals with a CTreeCtrl object. Should the run-time class be a CListCtrl, the function deals with a CListCtrl object. Depending on the decision made, the appropriate request processing function is called.

C++
ProcessInputRequest(CListBox  *pLB,   CPoint point);
ProcessInputRequest(CListCtrl *pLC,   CPoint point);
ProcessInputRequest(CTreeCtrl *pTree, CPoint point);

Each of these functions estimates the position of the hit item and invokes the modal LocalInputDlg.

Supporting the LVN_ENDLABELEDIT Notification for list-view Controls

eFotografo made a good point (see his message in the FAQ section below). In case of CListCtrl objects, the corresponding ProcessInputRequest() processing function needs to send a WM_NOTIFY message to control's parent, so that LVN_ENDLABELEDIT can be handled, if the application is needed to decide whether to accept or not the modification in the list-view (input validation).
C++
NMLVDISPINFO nminfo;
nminfo.hdr.code = LVN_ENDLABELEDIT;
nminfo.hdr.hwndFrom = pLC->GetSafeHwnd();
nminfo.hdr.idFrom = pLC->GetDlgCtrlID();
nminfo.item.iItem = info.iItem;
nminfo.item.iSubItem = info.iSubItem;
nminfo.item.pszText = m_strInput.GetBuffer(0);
nminfo.item.mask = LVIF_TEXT;
if (pLC->GetParent()->SendMessage(WM_NOTIFY, (WPARAM)IDDLOCALEDIT, 
	(LPARAM)(LPNMHDR)&nminfo))
{
    pLC->SetItemText(info.iItem, info.iSubItem, m_strInput.GetBuffer(0));
}

Depending on the value returned by SendMessage(WM_NOTIFY), the text in the list cell is modified according to the user input or the modification is rejected. This value (either 1 or 0) is decided within the handler for the LVN_ENDLABELEDIT notification:

C++
void YourAppDlg::YourHandlerFor_LVN_ENDLABELEDIT (NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
    CString itemtext(pDispInfo->item.pszText); // do something with it
    *pResult = TRUE; // accept the changes
    MessageBox((LPCTSTR)pDispInfo->item.pszText, 
		(LPCTSTR)"Accepted input"); // testing only
}

combo image

C++
void YourAppDlg::YourHandlerFor_LVN_ENDLABELEDIT (NMHDR* pNMHDR, LRESULT* pResult) 
{
    NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
    CString itemtext(pDispInfo->item.pszText); // do something with it
    *pResult = FALSE; // reject the changes
    MessageBox((LPCTSTR)pDispInfo->item.pszText, 
		(LPCTSTR)"Accepted input"); // testing only
}

combo image

In case no validation action is taken in the application using the LVN_ENDLABELEDIT notification mechanism, i.e. no entry is provided in the message map of application's dialog:

C++
BEGIN_MESSAGE_MAP(YourAppDlg, CDialog)
	//{{AFX_MSG_MAP(YourAppDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	<s>ON_NOTIFY(LVN_ENDLABELEDIT, .....)</s>
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

pLC->GetParent()->SendMessage(WM_NOTIFY) in the ProcessInputRequest() code for CListCtrl always returns 0 and LocalInputDlg loses functionality. For that reason, the LIWNE_USE_LVN_ENDLABELEDIT preprocessor variable is used in LocalInputDlg.h:

C++
#ifndef LIWNE_USE_LVN_ENDLABELEDIT
//#define LIWNE_USE_LVN_ENDLABELEDIT
#endif

The header file comes with this definition commented out, assuming the LVN_ENDLABELEDIT event is not handled in your application. Please uncomment it should your application handle that event. The presence of that preprocessor variable turns on the code in ProcessInputRequest() for CListCtrl related to input validation. By removing it, the previously mentioned code is turned off and the pLC-> SetItemText(info.iItem, info.iSubItem, m_strInput.GetBuffer(0)) is always called to accomplish the local editing of the list-view cell.

History

  • Created: March 21, 2005
  • Updated: March 23, 2005, due to the welcome request of jdmulder and the excellent ideas of PJ Arends. Code lines not belonging to the author are marked as "courtesy of ...".
  • Updated: April 5, 2005 - support for "arrow" style and "in-line" style
  • Updated: March 10, 2006 - article content correction in the section "Using the LocalInputDlg 4.b.": dlgInput.DoLocalInput(pMsg,...) instead of dlgInput(pMsg,...) (fortunately, the source code is ok :-))
  • Updated: October 16, 2009 - VS2005 source code and LVN_ENDLABELEDIT notification support added, as a result of eFotografo's excellent remarks

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Europe Europe
More than 30 years of software development experience.
(also playing the SCRUM Master role depending on the project)

Comments and Discussions

 
GeneralMy vote of 5 Pin
xuplus28-Jul-10 23:43
xuplus28-Jul-10 23:43 
perfect work!
Thanks a lot!
GeneralRe: My vote of 5 Pin
Mircea Puiu23-May-11 4:37
Mircea Puiu23-May-11 4:37 
GeneralEnabling LVN_ENDLABELEDIT Pin
eFotografo9-Oct-09 6:05
professionaleFotografo9-Oct-09 6:05 
GeneralRe: Enabling LVN_ENDLABELEDIT Pin
Mircea Puiu9-Oct-09 21:47
Mircea Puiu9-Oct-09 21:47 
GeneralDebug ASSERT Pin
eFotografo8-Oct-09 7:29
professionaleFotografo8-Oct-09 7:29 
GeneralRe: Debug ASSERT Pin
Mircea Puiu9-Oct-09 2:30
Mircea Puiu9-Oct-09 2:30 
QuestionDoes not work with CListCtrl in a DialogBox Pin
RLoe3-Sep-09 4:42
RLoe3-Sep-09 4:42 
AnswerRe: Does not work with CListCtrl in a DialogBox Pin
Mircea Puiu6-Sep-09 23:41
Mircea Puiu6-Sep-09 23:41 
GeneralRe: Does not work with CListCtrl in a DialogBox Pin
RLoe7-Sep-09 12:49
RLoe7-Sep-09 12:49 
GeneralRe: Does not work with CListCtrl in a DialogBox Pin
Mircea Puiu15-Oct-09 2:36
Mircea Puiu15-Oct-09 2:36 
GeneralStill Assert Pin
domgom20-Jun-06 12:18
domgom20-Jun-06 12:18 
GeneralLimiting use of this class Pin
jouwpaard11-May-06 1:32
jouwpaard11-May-06 1:32 
GeneralRe: Limiting use of this class Pin
Mircea Puiu11-May-06 4:41
Mircea Puiu11-May-06 4:41 
GeneralRe: Limiting use of this class Pin
jouwpaard11-May-06 7:27
jouwpaard11-May-06 7:27 
GeneralRe: Limiting use of this class Pin
Mircea Puiu11-May-06 7:49
Mircea Puiu11-May-06 7:49 
GeneralCListBox Pin
hanno2527-Jan-06 23:02
hanno2527-Jan-06 23:02 
GeneralRe: CListBox Pin
Mircea Puiu28-Jan-06 8:07
Mircea Puiu28-Jan-06 8:07 
GeneralRe: CListBox Pin
hanno2530-Jan-06 2:40
hanno2530-Jan-06 2:40 
GeneralRe: CListBox Pin
Mircea Puiu15-Feb-06 5:23
Mircea Puiu15-Feb-06 5:23 
GeneralDisable ClistCtrl Pin
mbks19-Jul-05 4:48
mbks19-Jul-05 4:48 
GeneralRe: Disable ClistCtrl Pin
Mircea Puiu19-Jul-05 5:50
Mircea Puiu19-Jul-05 5:50 
GeneralTHANKS Pin
afelsan15-Jul-05 1:59
afelsan15-Jul-05 1:59 
GeneralRe: THANKS Pin
Mircea Puiu19-Jul-05 5:51
Mircea Puiu19-Jul-05 5:51 
GeneralI'm not alone! Pin
araud31-Mar-05 18:31
araud31-Mar-05 18:31 
GeneralRe: I'm not alone! Pin
Mircea Puiu31-Mar-05 20:17
Mircea Puiu31-Mar-05 20:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.