Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Local Input With No Effort

0.00/5 (No votes)
16 Oct 2009 1  
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:
      if (dlgInput.DoLocalInput(pMsg, true)) return TRUE;

      should the chosen style be "arrow", or:

      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.

    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).
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:

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

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:

BEGIN_MESSAGE_MAP(YourAppDlg, CDialog)
	//{{AFX_MSG_MAP(YourAppDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_NOTIFY(LVN_ENDLABELEDIT, .....)
	//}}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:

#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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here