The EX31B Record View Example

The EX31B example is an "add-change-delete" application that's different from the Access model. The user must explicitly add, update, and delete records. Even if you prefer the Access-style behavior, you can learn a lot about the CRecordView class by going through the steps in the EX31B example.

Here are the steps for building the EX31B example:

  1. Run AppWizard to produce \vcpp32\ex31b. As you move through the AppWizard steps, select Single Document Interface (Step 1 dialog box) and Database View Without File Support (Step 2). In the Step 2 dialog box, also click the Data Source button and choose the ODBC datasource named Student Registration. Choose Dynaset as the Recordset Type, then select the Instructor table. Finally, deselect Printing And Print Preview (Step 4). The options and the default class names are shown below.

  2. Add edit controls to the IDD_EX31B_FORM template. Use the IDs IDC_ID, IDC_NAME, and IDC_ROOM, and position the controls as shown here.

  3. Use ClassWizard to link the edit controls to the recordset data members. To add a data member, click on the Member Variables tab and choose the ID corresponding to the edit box for each variable. Click the Add Variable button, and click the arrow in the Member Variable Name combo box to display a list of variables. Select only the appropriate variable, as shown here.

    When you're finished adding variable names for each edit box, you'll see a screen like the one shown here.

    Click to view at full size.

  4. Build and test the EX31B application.You should have a working read-only database application that looks like Figure 31-4. Use the toolbar buttons to sequence through the instructor records.

  5. Back up your database. Now you're going to include the logic to add, change, and delete records. It's a good idea to make a copy of the STDREG32.MDB file first. That way you have something to refer back to after you delete all the records.

  6. Add menu commands. Add the following items to the Record pop-up menu in the IDR_MAINFRAME menu. Also, use ClassWizard to map the commands to the specified CEx31bView class members.
    Menu CommandCommand IDCommand HandlerUpdate Command UI Handler
    Add RecordID_RECORD_ADD OnRecordAdd 
    Clear FieldsID_RECORD_CLEARFIELDS OnRecordClearfields 
    Delete RecordID_RECORD_DELETE OnRecordDeleteOnUpdateRecordDelete
    Update RecordID_RECORD_UPDATEOnRecordUpdateOnUpdateRecordUpdate

  7. Add and override the OnMove function in the CEx31bView class. The CRecordView::OnMove function does the work of updating the database when the user moves out of a record. Because we don't want this behavior, we must override the function as follows:

    BOOL CEx31bView::OnMove(UINT nIDMoveCommand) 
    {
        switch (nIDMoveCommand)
        {
        case ID_RECORD_PREV:
            m_pSet->MovePrev();
            if (!m_pSet->IsBOF())
                break;
    
        case ID_RECORD_FIRST:
            m_pSet->MoveFirst();
            break;
    
        case ID_RECORD_NEXT:
            m_pSet->MoveNext();
            if (!m_pSet->IsEOF())
                break;
            if (!m_pSet->CanScroll()) {
                // Clear screen since we're sitting on EOF
                m_pSet->SetFieldNull(NULL);
                break;
            }
    
        case ID_RECORD_LAST:
            m_pSet->MoveLast();
            break;
    
        default:
            // unexpected case value
            ASSERT(FALSE);
        }
    
        // Show results of Move operation
        UpdateData(FALSE);
        return TRUE;
    }

    Also, add the declaration for this overridden function to the ex31bView.h header file.

  8. Edit the menu command handlers.The following functions call various CRecordset member functions to edit the database. To add a record, you must call CRecordset::AddNew, followed by Update. To modify a record, you must call CRecordset::Edit, followed by Update. When you add a new record to the database, you should call CRecordset::MoveLast because the new record is always added to the end of the dynaset.

    If you have a sorted recordset (or if your ODBC driver doesn't put added records in the recordset), you should call CRecordset::Requery to completely regenerate the recordset. In that case, there's no convenient way to position the cursor on the newly added record, and that's a basic problem with SQL.

    Add the following boldface code:

    void CEx31bView::OnRecordAdd()
    {
        m_pSet->AddNew();
        UpdateData(TRUE);
        if (m_pSet->CanUpdate()) {
            m_pSet->Update();
        }
        if (!m_pSet->IsEOF()) {
            m_pSet->MoveLast();
        }
        m_pSet->Requery(); // for sorted sets
        UpdateData(FALSE);
    }
    
    void CEx31bView::OnRecordClearfields() 
    {
        m_pSet->SetFieldNull(NULL);
        UpdateData(FALSE);
    }
    
    void CEx31bView::OnRecordDelete() 
    {
        CRecordsetStatus status;
        try {
            m_pSet->Delete();
        }
        catch(CDBException* e) {
            AfxMessageBox(e->m_strError);
            e->Delete();
            m_pSet->MoveFirst(); // lost our place!
            UpdateData(FALSE);
            return;
        }
        m_pSet->GetStatus(status);
        if (status.m_lCurrentRecord == 0) {
            // We deleted last of 2 records
            m_pSet->MoveFirst();
        }
        else {
            m_pSet->MoveNext();
        }
        UpdateData(FALSE);
    }
    
    void CEx31bView::OnUpdateRecordDelete(CCmdUI* pCmdUI) 
    {
        pCmdUI->Enable(!m_pSet->IsEOF());
    }
    
    void CEx31bView::OnRecordUpdate() 
    {
        m_pSet->Edit();
        UpdateData(TRUE);
        if (m_pSet->CanUpdate()) {
            m_pSet->Update();
        }
    // should requery if key field changed
    }
    
    void CEx31bView::OnUpdateRecordUpdate(CCmdUI* pCmdUI) 
    {
        pCmdUI->Enable(!m_pSet->IsEOF());
    }

  9. Build and test the EX31B application again. Now you can add, change, and delete records. Observe what happens if you try to add a record with a duplicate key. You get an error message that comes from an exception handler inside the framework. You can add try/catch logic in OnRecordAdd to customize the error processing.