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

The Ultimate Grid Edit Functionality

0.00/5 (No votes)
25 Aug 2007 1  
The Ultimate Grid provides for built-in edit notifications as well as customizable edit functionality

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

Introduction

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.

Basic Editing

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;    // code omitted below ...


     }
     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 );

     // - USE_CHARLIMIT is set on cells that limit number of 


     // - input characters to 4


     // - USE_NUMLIMIT is set on cells that only allow numbers with total 


     // - value < 500


     if ( cell.GetParam() == USE_CHARLIMIT )
     {    // check if user has reached the 4 character limit


          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;
               // check if the character pressed is a numeric digit 


               // or a dot, and make sure that the dot only is present 


               // once.  Also only allow two fractional digits.


               if (( *vcKey >= 48 && *vcKey <= 57 && 
                   *vcKey == '.' && pointPos == -1 ) ||
                   fraction <= 2 || *vcKey == 8 )
               {
                    int stSel, edSel;

                    ((CEdit*)edit)->GetSel( stSel, edSel );
                    // make sure that the new value is under 500


                    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 );    // this code fires for 


        // cells that are intended to contain the mask for the cell below


        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;
}

Advanced Editing

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.

History

Initial CodeProject release August 2007.

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