Visit the Ultimate Grid main page for an overview and configuration guide to the Ultimate Grid library.
Source code and project files for this sample are included in the Demos\EditingDemo directory of the sample projects download.
Contents
The Ultimate Grid editing capabilities allow for a great deal of flexibility in data entry and validation. Any number of edit controls and/or validation routines can be coded and invoked based on a cells properties, column preferences, etc.
For this discussion of the grids edit capability, we'll focus first on working with the basic edit capabilities of the grid control (using the default 'built-in' edit objects) then move on to working with alternate edit classes.
Let's assume a grid (CUGCtrl
) derived class (MyCug
) is active and its OnSetup
called creating a number of rows and columns (as described in the Beginner's Guide). By default, editing is enabled on cells in the grid. There are various ways this can be disallowed - cell set to read only, cell type doesn't support text edits, OnEditStart
rejects the attempted edit, etc., but we'll assume a basic grid.
CUGCtrl::StartEdit
is usually called in response to a user action - for example, your derived class can call StartEdit
in its OnCharDown
, OnDClicked
, etc. You can also call an overloaded StartEdit
which will start editing on a cell at a given column and row. You can also pass in an initial key (useful when starting in response to a key stroke).
StartEdit
will return UG_SUCCESS
if the cell type can edit text, is not read only, and the OnEditStart
methods of both the current datasource and the derived control class return TRUE.
At this point, the default edit should be active in the cell, with the correct font etc. If the cell has a mask set (CUGCell::SetMask
) then the default masked edit will be invoked.
Four basic notifications are available as overridables to deal with edit events in the grid:
OnEditStart
CUGCtrl::OnEditStart(int col, int row, CWnd **edit)
This override is called first for the current datasource then for the grid. Code placed in OnEditStart
can abort the edit by returning FALSE. It can also modify the style of the edit control, and/or replace the default edit with an instance of another CUGEditBase
derived control (more on this in the next section).
In this sample snippet from the demo, a cell may have an application defined value set to its param
member to determine whether it should be restricted to upper or lower case or password entry:
int MyCug::OnEditStart(int col, long row,CWnd **edit)
{
CUGCell cell;
GetCellIndirect( col, row, &cell );
...
if ( cell.GetParam() == USE_UPPER )
{
((CEdit*)*edit)->ModifyStyle( 0, ES_UPPERCASE );
}
else if ( cell.GetParam() == USE_LOWER )
{
((CEdit*)*edit)->ModifyStyle( 0, ES_LOWERCASE );
}
else if ( cell.GetParam() == USE_PASSWORD )
{
dwStyle = ES_PASSWORD;
}
else
{
((CEdit*)*edit)->ModifyStyle( ES_UPPERCASE, 0 );
((CEdit*)*edit)->ModifyStyle( ES_LOWERCASE, 0 );
((CEdit*)*edit)->ModifyStyle( ES_PASSWORD, 0 );
}
...
OnEditVerify
int CUGCtrl::OnEditVerify(int col, long row, CWnd *edit,
UINT *vcKey)
OnEditVerify
is called for every key that is pressed while the edit control has focus. This allows you to process and perform early validation on what the user is entering into your cell. You also have access to a pointer to the edit control itself, and the ability to reject the keypress by returning FALSE
.
This code is also from the demo, and deals with cells that have a flag set (again, via CUGCell::SetParam
) indicating that the cell should be checked for text length or numeric limit:
int MyCug::OnEditVerify(int col, long row,CWnd *edit,UINT *vcKey)
{
CUGCell cell;
GetCellIndirect( col, row, &cell );
if ( cell.GetParam() == USE_CHARLIMIT )
{
if ( edit->GetWindowTextLength() >= 4 && *vcKey != 8 )
return FALSE;
}
else if ( cell.GetParam() == USE_NUMLIMIT )
{
CString string;
edit->GetWindowText( string );
int pointPos = string.Find( "." );
int fraction = 0;
if ( pointPos >= 0 )
fraction = string.GetLength() - pointPos;
string += (char)*vcKey;
if (( *vcKey >= 48 && *vcKey <= 57 &&
*vcKey == '.' && pointPos == -1 ) ||
fraction <= 2 || *vcKey == 8 )
{
int stSel, edSel;
((CEdit*)edit)->GetSel( stSel, edSel );
if (( atol( string ) > 500 ) && (
stSel == edSel ))
return FALSE;
}
else
return FALSE;
}
return TRUE;
}
OnEditFinish
CUGCtrl::OnEditFinish(int col, int row, CWnd **edit, LPCTSTR string,
BOOL cancelFlag)
This call occurs before OnEditContinue()
is fired, and after the CUGDataSource::OnEditFinish()
function is called (assuming the datasource call did not return FALSE
).
This notification is called when editing of cell is ending. This routine can be used to cancel the changes that have occurred based on the data the user entered. To cancel the changes to the cell return FALSE, forcing the user back into the cell that was being edited. If you want to change the data after an edit has occurred, you can use the OnSetCell
method of your derived class.
The cancelFlag
parameter will be set to TRUE
if the user wishes to cancel the edit (e.g. has pressed the 'Escape' key).
Here, again from the demo, is a sample OnEditFinish
routine:
int MyCug::OnEditFinish(int col, long row,CWnd *edit,LPCTSTR string,BOOL cancelFlag)
{
UNREFERENCED_PARAMETER(edit);
CString tempStr( string );
CUGCell cell;
GetCellIndirect( col, row, &cell );
if ( cell.GetParam() == USE_PASSWORD )
{
QuickSetText( col + 2, row,
"Entered password was: " + tempStr );
RedrawCell( col + 2, row );
}
else if ( cell.GetParam() == USE_FINISHTEST && cancelFlag != TRUE )
{
if ( tempStr == "" )
{
MessageBox( "This field cannot be left blank\n"
"please make sure a value is entered.\n"
"Or hit ESC to cancel edit.",
"Input error", MB_ICONEXCLAMATION );
return FALSE;
}
}
else if ( cell.GetParam() == USE_SETMASK && cancelFlag != TRUE )
{
GetCellIndirect( col, row + 1, &cell );
cell.SetMask( string );
cell.SetText( "<type />" );
SetCell( col, row + 1, &cell );
RedrawCell( col, row + 1 );
}
return TRUE;
}
OnEditContinue
int CUGCtrl::OnEditContinue(int oldcol, long oldrow, int* newcol,
long* newrow)
This notification is called when the user presses the 'tab', 'enter', or 'up/down' arrow keys, and occurs after OnEditFinish
if the user hasn't cancelled the edit.
The grid normally will remain in 'edit mode' when editing is done for the current cell, and resume editing in the next active cell (e.g. the next cell in the row if the user has pressed the tab key). This override gives you a chance to change the next active cell by modifying the newcol
and newrow
parameters, or tell the grid that editing should not be started on the next cell by returning FALSE
. Returning FALSE
will cause the current cell to retain focus after editing is completed.
If you want to decide whether or not the edit should be allowed based on the data the user entered then use the OnEditFinish()
function instead.
The editing demo looks at the contents of the cell being navigated to and clears the text if it contains the "<Type here>" prompt.
int MyCug::OnEditContinue(int oldcol,long oldrow,int* newcol,long* newrow)
{
UNREFERENCED_PARAMETER(oldcol);
UNREFERENCED_PARAMETER(oldrow);
CUGCell cell;
GetCellIndirect( *newcol, *newrow, &cell );
if ( CString(cell.GetText()) == "<type />" )
{
cell.SetText( "" );
SetCell( *newcol, *newrow, &cell );
}
return TRUE;
}
The Ultimate Grid control class (CUGCtrl
) maintains two member edit objects, CUGEdit
and CUGMaskedEdit
which are derived from CUGEditBase
and used as defaults.
New edit controls can be derived from the CUGEditBase
class. CUGEditBase
is a CEdit
derived class that overrides the WindowProc
function to provide the default behavior for edit classes that are to be used with the grid. Because CUGEditBase
is derived from CEdit
, it is usually sufficient to change the base class specification of a CEdit
derived class to CUGEditBase
to convert an existing edit to one that can be used with the grid.
There are two ways alternate edit and masked edit classes can be used as edit controls for the grid.
If you want the new class(es) to be the default edit control(s) for the entire grid, you can call CUGCtrl::SetNewEditClass
and/or CUGCtrl::SetNewMaskedEditClass
with pointers to instances of these.
If you want to use certain edit controls for specific cell edits, you can do this in the OnEditStart
routine. This sample (yes, from the demo - talk about code re-use!) creates a CUTMaskedEdit
object and vectors the pointer to the edit control so that it will be used instead of the default:
int MyCug::OnEditStart(int col, long row,CWnd **edit)
{
CUGCell cell;
GetCellIndirect( col, row, &cell );
DWORD dwStyle = 0;
...
if ( cell.GetParam() == USE_SETCOXMASK )
{
if ( ! m_cutMaskEdit.GetSafeHwnd())
{
m_cutMaskEdit.Create( WS_CHILD|WS_VISIBLE, CRect(0,0,0,0), this,
COXMASK_ID );
m_cutMaskEdit.m_ctrl = this;
}
*edit = &m_cutMaskEdit;
return TRUE;
}
...
The Ultimate Grid ships with four additional edit controls that were adopted from the Ultimate Toolbox library - CUTEdit
, CUTMaskedEdit
, CUTNumericEdit
, and CUTCurrencyEdit
. The code for these will be found in the EditControls directory of the source download.
Initial CodeProject release August 2007.