Virtual Developer Workshop: Containerized Development with Docker
Environment: VC++ 5.0 (SP3) NT 4.0 (SP4) - CGridCtrl version 1.12
Since I posted my MFC grid control I've had a lot of requests asking how to use other methods of editing cells in the grid. I've created a demo project that shows how to replace the default editing with a dropdown list of choices. This is achieved by deriving a new class from CGridCtrl and overridding CGridCtrl::CreateInPlaceEditControl.
Thanks go to Roelf Werkman for his work in
extending the CInPlaceList to allow the CBS_DROPDOWN and CBS_SIMPLE styles
to be used.
The first step is to derive a new class from CGridCtrl - I call it CComboGridCtrl - and override CreateInPlaceEditControl. There are a number of things to be aware of when you override this function. CreateInPlaceEditControl receives the row and column to be edited, the dimensions and location of the in-place edit control, the style of control to create, the original text in the cell plus the initial character that caused editing to commence (or VK_LBUTTON if the mouse was clicked on the current cell).
- Your editing control must self-delete when it loses input focus.
- Your editing object should handle mouse keys in a way that allows
the user to navigate between cells while editing. If the control gets an
arrow key for instance, it should cancel editing and return the last key
it encountered back to the grid via the GVN_ENDLABELEDIT notification
Note that the grid control (as of version 1.08) accepts Ctrl+<arrows> for navigation, so if your in-place edit control needs to use the arrows, you can always reserve Ctrl+<arrows> to move to other cells.
To pass the necessary data back to the parent grid once editing has been completed,
use the following code snippet in your in-place edit control:
// Send Notification to parent
dispinfo.hdr.hwndFrom = GetSafeHwnd(); // Handle of window
dispinfo.hdr.idFrom = GetDlgCtrlID(); // control ID
dispinfo.hdr.code = GVN_ENDLABELEDIT; // Message
dispinfo.item.mask = LVIF_TEXT|LVIF_PARAM; // Not used in default mode
dispinfo.item.row = m_nRow; // Row - previously stored
dispinfo.item.col = m_nCol; // column - previously stored
dispinfo.item.szText = str; // New text
dispinfo.item.lParam = (LPARAM) m_nLastChar; // Last character encountered
I pass the character that initiated editing to the edit control itself so that it can deal with keys appropriately. For instance in the default edit control, if the edit control is passed a normal character or arrow key as the "initiating" key, then it will cease editing when it encounters an arrow key (to allow navigation using the keyboard). If the edit control is passed VK_LBUTTON (meaning editing was initiated by a mouse click) then arrow keys will not cause the editing to cease.
Once you have an edit control ready to be used, override CreateInPlaceEditControl
and use your own edit control instead of the default in-place edit control.
void CComboGridCtrl::CreateInPlaceEditControl(CRect& rect, DWORD dwStyle, UINT nID,
int nRow, int nCol,
LPCTSTR szText, int nChar)
// InPlaceList and auto-deletes itself
new CInPlaceList(this, rect,
//CBS_DROPDOWNLIST, // Uncomment for dropdown list style
CBS_DROPDOWN, // Uncomment for dropdown style
//CBS_SIMPLE, // Uncomment for simple style
nID, // ID of control being created
Items, szText, nChar);
The sample project includes the source code for the CInPlaceList control presented
Last Updated: Aug 11, 1998.