📄 vcg15.htm
字号:
<BR>
<BR>
<LI>When you use bound controls to update existing records, the application places a lock on the current page (Access, Btrieve, and SQL Server databases) or record (dBASE, FoxPro, and Paradox databases) for the duration of the editing process. If the person who is editing the record goes to lunch without completing the edit, no one else can edit the record (or any record in the same 2K page) for the duration.
<BR>
<BR>
</UL>
<P>The following sections describe the use of edit boxes in conjunction with an AppWizard-created database application. The methods used here are for the sake of simplicity; production-grade applications use command buttons to commit or cancel edits and to manipulate record-pointers.
<BR>
<BR>
<A NAME="E70E75"></A>
<H5 ALIGN=CENTER>
<CENTER>
<FONT SIZE=4 COLOR="#FF0000"><B>Emulating an Access Continuous Subform in Visual C++</B></FONT></CENTER></H5>
<BR>
<P>If you're converting an Access database application to Visual C++, you often need to emulate the continuous subform controls that are commonly employed in Access applications. There are several ways to do this. This chapter looks at two methods. The first method to emulate an Access continuous subform is to use a dialog box that has individual edit boxes for each displayed record (row) and field. A second method is to use Visual C++ 4's OLE Grid control. A third method (not discussed in this chapter) is to use a ListView control.
<BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<HR ALIGN=CENTER>
<BR>
<NOTE><B>NOTE</B>
<BR>
<BR>Both of the sample programs in this chapter use the Access version of the Northwind Traders database, accessed using ODBC with the ODBC name of Northwind Access. If you've installed the Northwind Traders database using a different name, you can modify the source dataset name to reflect the name you used for your Northwind sample database as necessary. This name is found in the GetDefaultConnect function in the CRecordset class implementation, as this example shows:</NOTE>
<BR>
<PRE>
<FONT COLOR="#000080">CString CContinusingGridControlSet::GetDefaultConnect()
{
return _T("ODBC;DSN=Northwind Access");
}</FONT></PRE>
<HR ALIGN=CENTER>
</BLOCKQUOTE></BLOCKQUOTE><BR>
<P>Figure 15.1 shows an Access continuous subform. Figure 15.2 shows the Visual C++ version of this subform. Figure 15.3 shows a Visual C++ 4 OLE Grid control version of the program shown in Figure 15.2.
<BR>
<P><B><A HREF="15vcg01.gif" tppabs="http://202.113.16.101/%7eeb%7e/Database%20Developer's%20Guide%20with%20Visual%20C++%204,%20Second%20Edition/15vcg01.gif">Figure 15.1. An Access continuous subform.</A></B>
<BR>
<P>In Figure 15.2, the form's Product Name, English Name, Unit Price, Units in stock, and Units on Order are text-box controls. The Visual C++ code you write to fill the control arrays is similar to the C++ code needed to fill in a single control.
<BR>
<P><B><A HREF="15vcg02.gif" tppabs="http://202.113.16.101/%7eeb%7e/Database%20Developer's%20Guide%20with%20Visual%20C++%204,%20Second%20Edition/15vcg02.gif">Figure 15.2. A Visual C++ form that emulates an Access continuous subform.</A></B>
<BR>
<P>In Figure 15.3, all the form's fields are contained in a single Visual C++ 4 OLE Grid control. This OCX control could be supplemented with edit boxes to let the user modify the values displayed. Unlike the examples shown in Figures 15.1 and 15.2, in-place editing isn't supported using an OLE Grid control.
<BR>
<P><B><A HREF="15vcg03.gif" tppabs="http://202.113.16.101/%7eeb%7e/Database%20Developer's%20Guide%20with%20Visual%20C++%204,%20Second%20Edition/15vcg03.gif">Figure 15.3. A Visual C++ 4 OLE Grid control that emulates an Access continuous subform.</A></B>
<BR>
<P>The most important thing to remember about a continuous form-type view is that more than one record from the recordset must be accessed at any one time. In Figure 15.2, this is done by simulation. Ten rows are displayed, and 10 rows in the recordset are accessed sequentially to fill in the 10 rows in the display.
<BR>
<P>The OLE Grid example in Figure 15.3 displays more than 10 rows. When I created this sample program, it was impossible to determine exactly how many rows would be visible to the user.
<BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<HR ALIGN=CENTER>
<BR>
<NOTE><B>NOTE</B>
<BR>
<BR>The OLE Grid control has some limitations. The maximum number of rows is 16,352, and the maximum number of columns is 5,450. It's unlikely that your application will exceed the column limitation, but it's possible to generate a query that can return more than 16,352 rows of data. You must check for this situation and warn the user that not all records returned are displayed.</NOTE>
<BR>
<HR ALIGN=CENTER>
</BLOCKQUOTE></BLOCKQUOTE>
<BLOCKQUOTE>
<BLOCKQUOTE>
<HR ALIGN=CENTER>
<BR>
<NOTE><B>NOTE</B>
<BR>
<BR>Contin, the sample application shown in Figure 15.2, is located by default in the CHAPTR15 directory on the CD that comes with this book. The project called Contin using Grid Control is also found in this directory.</NOTE>
<BR>
<HR ALIGN=CENTER>
</BLOCKQUOTE></BLOCKQUOTE>
<P>Listing 15.1 shows the code you need to write in order to fill a simulated continuous subform with data from the table or query specified when the application was created. You need to explicitly handle null values, because the Microsoft versions of text boxes and edit controls don't convert null values to empty strings.
<BR>
<P>All the necessary changes are made in two files: the view class source file (in this program, CONTINVW.CPP) and the header file for the view class (CONTINVW.H). In Listing 15.1, the code that lets you access multiple rows appears in bold.
<BR>
<P>
<FONT COLOR="#000080"><B>Listing 15.1. Code to emulate an Access subform using edit boxes.</B></FONT>
<BR>
<PRE>
<FONT COLOR="#000080">// contivw.cpp : implementation of the CContinView class
//
#include "stdafx.h"
#include "contin.h"
#include "contiset.h"
#include "contidoc.h"
#include "contivw.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CContinView
IMPLEMENT_DYNCREATE(CContinView, CRecordView)
BEGIN_MESSAGE_MAP(CContinView, CRecordView)
//{{AFX_MSG_MAP(CContinView)
// NOTE: ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CRecordView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CRecordView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CContinView construction/destruction
CContinView::CContinView()
: CRecordView(CContinView::IDD)
{
//{{AFX_DATA_INIT(CContinView)
m_pSet = NULL;
//}}AFX_DATA_INIT
// TODO: Add construction code here
<B> for (int i = 0; i < 10; i++)</B>
<B> {</B>
<B> m_EnglishName[i] = "";</B>
<B> m_ProductName[i] = "";</B>
<B> m_UnitPrice[i] = "";</B>
<B> m_UnitsInStock[i] = 0;</B>
<B> m_UnitsOnOrder[i] = 0;</B>
<B> }</B>
<B> m_ArraySet = FALSE;</B>
}
CContinView::~CContinView()
{
}
<B>BOOL CContinView::OnMove(UINT nIDMoveCommand)</B>
<B>{</B>
<B>int i;</B>
<B>int nStepBack;</B>
<B>//---------START OF DBVIEW.CPP OnMove()...</B>
<B> if (CDatabase::InWaitForDataSource())</B>
<B> {</B>
<B>#ifdef _DEBUG</B>
<B> if (afxTraceFlags & 0x20)</B>
<B> TRACE0("Warning: ignored move request\n");</B>
<B>#endif // _DEBUG</B>
<B> return TRUE;</B>
<B> }</B>
<B> if (m_pSet->CanUpdate())</B>
<B> {</B>
<B> if (!UpdateData())</B>
<B> return TRUE;</B>
<B> nStepBack = 0;</B>
<B> for (i = 0; i < 10; i++)</B>
<B> {// Save if the current record, and then get next one!</B>
<B> m_pSet->Edit();</B>
<B> m_pSet->m_English_Name = m_EnglishName[i];</B>
<B> m_pSet->m_Product_Name = m_ProductName[i];</B>
<B> m_pSet->m_Unit_Price = m_UnitPrice[i];</B>
<B> m_pSet->m_Units_In_Stock = m_UnitsInStock[i];</B>
<B> m_pSet->m_Units_On_Order = m_UnitsOnOrder[i];</B>
<B> m_pSet->Update();</B>
<B> if (!m_pSet->IsEOF())</B>
<B> {</B>
<B> TRY</B>
<B> {// Use old-style exceptions for Visual C++ 1.5x</B>
<B> m_pSet->MoveNext();</B>
<B> --nStepBack;</B>
<B> }</B>
<B> CATCH(CDBException, e)</B>
<B> {// Died. Should use message box to user!</B>
<B> TRACE("MoveNext() fail Ret = %d Error '%s', cause '%s'\n",</B>
<B> e->m_nRetCode,</B>
<B> (const char *)e->m_strError,</B>
<B> (const char *)e->m_strStateNativeOrigin);</B>
<B> }</B>
<B> END_CATCH</B>
<B> }</B>
<B> else</B>
<B> {</B>
<B> break;</B>
<B> }</B>
<B> }</B>
<B>// Restore the record pointer! Take nStepBack giant steps back!</B>
<B> TRY</B>
<B> {// Use old-style exceptions for Visual C++ 1.5x</B>
<B> m_pSet->Move(nStepBack); // Back to original record...</B>
<B> }</B>
<B> CATCH(CDBException, e)</B>
<B> {// Died. Should use message box to user!</B>
<B> TRACE("Move(nStepBack) failed Ret = %d Error '%s', cause '%s'\n",</B>
<B> e->m_nRetCode,</B>
<B> (const char *)e->m_strError,</B>
<B> (const char *)e->m_strStateNativeOrigin);</B>
<B> }</B>
<B> END_CATCH</B>
<B> }</B>
<B> switch (nIDMoveCommand)</B>
<B> {</B>
<B> case ID_RECORD_PREV:</B>
<B> m_pSet->MovePrev();</B>
<B> if (!m_pSet->IsBOF())</B>
<B> break;</B>
<B> case ID_RECORD_FIRST:</B>
<B> m_pSet->MoveFirst();</B>
<B> break;</B>
<B> case ID_RECORD_NEXT:</B>
<B> m_pSet->MoveNext();</B>
<B> if (!m_pSet->IsEOF())</B>
<B> break;</B>
<B> if (!m_pSet->CanScroll())</B>
<B> {</B>
<B> // Clear out screen since we're sitting on EOF</B>
<B> m_pSet->SetFieldNull(NULL);</B>
<B> break;</B>
<B> }</B>
<B> case ID_RECORD_LAST:</B>
<B> m_pSet->MoveLast();</B>
<B> break;</B>
<B> default:</B>
<B> // Unexpected case value</B>
<B> ASSERT(FALSE);</B>
<B> }</B>
<B>//---------END OF DBVIEW.CPP OnMove()...</B>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -