Click here to Skip to main content
Click here to Skip to main content

The Ultimate Grid Edit Functionality

, 25 Aug 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

The Ultimate Toolbox
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.
 
Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
Group type: Organisation

394 members


Comments and Discussions

 
GeneralCenter Vertically Pinmembermluri21-May-09 0:51 
GeneralEdit the cells that attach a record of the database PinmemberDuc Do26-Sep-08 12:51 
GeneralRe: Edit the cells that attach a record of the database PineditorTim Deveaux26-Sep-08 14:04 
The call to Open in the tutorial should default to opening the database as a dynaset, which is required for editing.
 
You may also need to call CUGDAODataSource::SetOption with an option id of UGDAO_OPT_ALLOWEDITS, with TRUE as the second parameter.
 
Then a call to StartEdit (for example in the OnLClicked notification of your derived grid) should allow editing of existing cells (unless the cell is readonly or the data can't be edited - e.g. an autonumber column):
 
void MyCug::OnLClicked(int col,long row,int updn,RECT *rect,POINT *point,int processed){
     if (!updn) 
         StartEdit();
}

 
Adding new rows for editing can be tricky. Following the naming in the tutorial, you could try:
 
if(m_grid.m_data.AddDefaultRow() == UG_SUCCESS) {
     m_grid->GotoRow(m_grid.m_data.GetNumRowsComplete()-1);
     // might want to GotoCol as well to skip auto or read only cols
     m_grid.StartEdit();
}
To avoid the GetNumRowsComplete call, it would probably be better if the datasource called m_ctrl->GotoRow(m_nCurrentRow) at the end of the AddDefaultRow method - would be more efficient, I think.
 
You can also take a look at the MyAccess sample in the Datasources/DAO directory - for appends, it can use a 'ghost row' option. It's a bit buggy, but might give you some ideas.
 
Tim

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 25 Aug 2007
Article Copyright 2007 by The Ultimate Toolbox
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid