Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 123 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
123
Dung lượng
2,34 MB
Nội dung
Figure 19-14 shows how data from the database ultimately gets to be displayed by the view. Figure 19-14 The transfer of data between the data members in the CProductSet object that correspond to fields in the Products table and the controls in the dialog box associated with the CProductView object is man- aged by the DoDataExchange() member of CProductView. The code in this function to do this is not in place yet because you first need to add the controls to the dialog that are going to display the data and then link the controls to the recordset data members. You will do that next. Creating the View Dialog The first step is to place the controls on the dialog box, so go to Resource View, expand the list of dialog resources, and double-click Idd_Dbsample_Form. You can delete the static text object with the TODO message from the dialog. If you right-click the dialog box, you can choose to view its properties, as shown in Figure 19-15. If you scroll down through the properties you’ll see that the Style property has been set to Child because the dialog box is going to be a child window and will fill the client area. The Border property has been set to None because if the dialog box is to fill the client area, it won’t need a border. You’ll add a static text control to identify each field from the recordset that you want to display, plus an edit control to display it. Database Table DoFieldExchange() member of the Recordset object transfers data between the DB and the recordset DoDataExchange() member of the view object transfers data between the recordset and the view. RecordSet Object View Object 943 Connecting to Data Sources 22_571974 ch19.qxp 1/20/06 11:34 PM Page 943 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 19-15 You can enlarge the dialog if necessary by dragging its borders. Then, place controls on the dialog as shown in Figure 19-16. Figure 19-16 944 Chapter 19 22_571974 ch19.qxp 1/20/06 11:34 PM Page 944 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com You can add the text to each static control by just typing it as soon as the control has been placed on the dialog. As you see, I have entered the text for each static control so that it corresponds to the field name in the database. It’s a good idea to make sure that all the edit controls have sensible and different IDs, so right-click each of them in turn to display and modify their properties. Figure 19-17 shows the properties for the control corresponding to Product ID. Figure 19-17 It’s helpful to use the field name as part of the control ID as this indicates what the control display. Figure 19-17 shows the ID for the first edit control in the title bar of the properties window after I have modified it. You can change the IDs for the other edit controls similarly. Because you are not intending to update the database in this example, you should make sure that the data displayed by each edit box can- not be modified from the keyboard. You can do that by setting the Read Only property for of each of the edit controls as True. The background to the edit boxes will then have a different color to signal that they cannot be altered, as shown in Figure 19-18. You can add other fields to the dialog box, if you want. The one that is most important for the rest of our example is the Product ID, so you must include that. Save the dialog and then move on to the last step: linking the controls to the variables in the recordset class. 945 Connecting to Data Sources 22_571974 ch19.qxp 1/20/06 11:34 PM Page 945 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 19-18 Linking the Controls to the Recordset As you saw earlier in Figure 19-14, getting the data from the recordset displayed by the appropriate con- trol is the job of the DoDataExchange() function in the CProductView class. The m_pSet member pro- vides a means of accessing the members of the CProductSet object that contains the fields retrieved from the database, so linking the controls to the data members of CProductSet is easy. MFC defines a range of DDX_Field functions at global scope that are specifically for exchanging data between a view and a recordset. In particular, the DDX_FieldText() function has overloaded versions that transfers a variety of types of data between a recordset field and an edit box in a CRecordView object. The types that can be exchanged by the DDX_FieldText() function are: short int UINT long DWORD float double CString COleDateTime COleCurrency When you call the DDX_FieldText() function you must supply four arguments: ❑ A CDataExchange object that determines the direction of data transfer— whether the data is to be transferred to or from the recordset. You just supply the pointer that is passed as the argu- ment to the DoDataExchange() function. ❑ The ID of the control that is the source or destination of the data. ❑ A reference to a field data member in the CRecordset object that is the source or destination of the data. ❑ A pointer to the CRecordset object with which data is to be exchanged. So to implement the transfer of data between the recordset and the control for the Product ID field, insert the following call of the DDX_FieldText() function in the body of the DoDataExchange() function: DDX_FieldText(pDX, IDC_PRODUCTID, m_pSet->m_ProductID, m_pSet); The first argument is the pDX argument that is passed to the DoDataExchange() function. The second argument is the ID for the first edit control in the dialog box for the view, the third argument uses the m_pSet member of the CProductView class to access the m_ProductID member of the recordset object, and the last argument is the pointer to the recordset object. 946 Chapter 19 22_571974 ch19.qxp 1/20/06 11:34 PM Page 946 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com You can therefore fill out the code for the DoDataExchange() function in the CProductView class like this: void CProductView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); DDX_FieldText(pDX, IDC_PRODUCTID, m_pSet->m_ProductID, m_pSet); DDX_FieldText(pDX, IDC_PRODUCTNAME, m_pSet->m_ProductName, m_pSet); DDX_FieldText(pDX, IDC_UNITPRICE, m_pSet->m_UnitPrice, m_pSet); DDX_FieldText(pDX, IDC_UNITSINSTOCK, m_pSet->m_UnitsInStock, m_pSet); DDX_FieldText(pDX, IDC_CATEGORYID, m_pSet->m_CategoryID, m_pSet); DDX_FieldText(pDX, IDC_UNITSONORDER, m_pSet->m_UnitsOnOrder, m_pSet); } The programming mechanism for data transfer between the database and the dialog box owned by the CProductView object is illustrated in Figure 19-19. Figure 19-19 Products Table in Sample Data RFX calls in DoFieldExchange() CProductSet U ni t s i nStoc k P roduct N ame Pr o d u c t I D m_ProductName m_ProductID m_UnitslnStock DDX calls in DoDataExchange() DDX calls in DoDataExchange() 947 Connecting to Data Sources 22_571974 ch19.qxp 1/20/06 11:34 PM Page 947 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com The recordset class and the record view class cooperate to enable data to be transferred between the database and the controls in the dialog box. The CProductSet class handles transfers between the database and its data members and CProductView deals with transfers between the data members of CProductSet and the controls in the dialog. Exercising the Example Believe it or not you can now run the example. Just build it in the normal way and then execute it. The application should display a window similar to the one shown in Figure 19-20. Figure 19-20 The CRecordView base class automatically implements toolbar buttons that step from one record in the recordset to the next or to the previous record. There are also toolbar buttons to move directly to the first or last record in the recordset. Of course, the products are displayed in a default sequence. It would be nice to have them sorted in categories and in product ID sequence within each category. Next, you’ll see how you can do that. Sorting a Recordset As you saw earlier, the data is retrieved from the database by the recordset, using an SQL SELECT state- ment that is generated by the framework using the GetDefaultSQL() member. You can add an ORDER BY clause to the statement generated by setting a value in the m_strSort member of CProductSet, which is inherited from CRecordSet. This causes the output table from the query to be sorted, based on the string stored in m_strSort. You need to set only the m_strSort member to a string that contains the field name or names that you want to sort on; the framework provides the ORDER BY keywords. Where you have multiple names, you separate them by commas. But where should you add the code to do this? 948 Chapter 19 22_571974 ch19.qxp 1/20/06 11:34 PM Page 948 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com The transfer of data between the database and the recordset occurs when the Open() member of the recordset object is called. In your program, the Open() function member of the recordset object is called by the OnInitialUpdate() member of the base class to the view class, CRecordView. You can, therefore, put the code for setting the sort specification in the OnInitialUpdate() member of the CProductView class, as follows: void CProductView::OnInitialUpdate() { m_pSet = &GetDocument()->m_productSet; m_pSet->m_strSort = “[CategoryID],[ProductID]”; // Set the sort fields CRecordView::OnInitialUpdate(); } You just set m_strSort in the recordset to a string containing the name of the category ID field followed by the name of the product ID field. Square brackets are useful, even when there are no blanks in a name, because they differentiate strings containing these names from other strings, so you can immedi- ately pick out the field names. They are, of course, optional if there are no blanks in the field name. Modifying the Window Caption There is one other thing you could add to this function at this point. The caption for the window would be better if it showed the name of the table being displayed. You can arrange for this to happen by adding code to set the title in the document object: void CProductView::OnInitialUpdate() { m_pSet = &GetDocument()->m_productSet; m_pSet->m_strSort = “[CategoryID],[ProductID]”; // Set the sort fields CRecordView::OnInitialUpdate(); // Set the document title to the table name if (m_pSet->IsOpen()) // Verify the recordset is open { CString strTitle = _T(“Table Name”); // Set basic title string CString strTable = m_pSet->GetTableName(); if(!strTable.IsEmpty()) // Verify we have a table name strTitle += _T(“: “) + strTable; // and add to basic title GetDocument()->SetTitle(strTitle); // Set the document title } } After checking that the recordset is indeed open, you initialize a local CString object with a basic title string. You then get the name of the table from the recordset object by calling its GetTableName() mem- ber. In general, you should check that you do get a string returned from the GetTableName() function. Various conditions can arise that can prevent a table name from being set — for instance, there may be more than one table involved in the recordset. After appending a colon followed by the table name you have retrieved to the basic title in strTitle, you set the result as the document title by calling the docu- ment’s SetTitle() member. 949 Connecting to Data Sources 22_571974 ch19.qxp 1/20/06 11:34 PM Page 949 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com If you rebuild the application and run it again, it works as before but with a new window caption as Figure 19-21 shows. The product IDs are in ascending sequence within each category ID, with the cate- gory IDs in sequence, too. Figure 19-21 Using a Second Recordset Object Now that you can view all the products in the database, a reasonable extension of the program would be to add the ability to view all the orders for any particular product. To do this, you’ll add another record- set class to handle order information from the database and a complementary view class to display some of the fields from the recordset. You’ll also add a button to the Products dialog to enable you to switch to the Orders dialog when you want to view the orders for the current product. This enables you to operate with the arrangement shown in Figure 19-22. The Products dialog box is the starting position where you can step backwards and forwards through all the available products. Clicking the Show Orders button switches you to the dialog where you can view all the orders for the current product. You can return to the Products dialog box by clicking the Show Products button. Adding a Recordset Class You can start by adding the recordset class for the orders; right-click DBSample in Class View and select Add > Class from the pop-up. Select MFC from the set of Visual C++ categories and MFC ODBC Consumer as the template. When you click the Add button in the Add Class dialog box that is displayed, you’ll see the MFC ODBC Consumer Wizard dialog box shown in Figure 19-23. 950 Chapter 19 22_571974 ch19.qxp 1/20/06 11:34 PM Page 950 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 19-22 Select the type of consumer as Snapshot by selecting the radio button and then click the Data Sources button to go to the Select Data Source dialog box, where you’ll be able to identify the data source; it should be on the Machine Data Source tab. When you have selected Northwind as the data source in the same way as you’ve seen previously, you’ll see the Select Database Object dialog box as shown in Figure 19-24. You’ll select two tables to associate with the new recordset class that you’re going to create, so hold the Ctrl key down and select the Orders and Order Details table names. You can then click the OK button to complete the selection process. This returns you to the MFC ODBC Consumer dialog box where you’ll see the class name and file names have been entered. You can change the class name to COrderSet and the file names in a corresponding way, as shown in Figure 19-25. Clicking on the Finish button completes the process and causes the COrderSet class to generate. EditProduct ID Edit Unit Price Edit Product Name Edit Category ID Edit Show Orders Pressing the Show Orders button will open the Orders dialog for the current product Pressing the Show Products button will return to the Products dialog Units In Stock Sample ed Units On Order This dialog will allow you to step through the products available This dialog will allow you to step through all the orders for a given product EditOrder ID Edit Quantity EditProduct ID Edit Customer ID Show Products 951 Connecting to Data Sources 22_571974 ch19.qxp 1/20/06 11:34 PM Page 951 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Figure 19-23 Figure 19-24 952 Chapter 19 22_571974 ch19.qxp 1/20/06 11:34 PM Page 952 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... file: // CCustomerView record view #pragma once class CCustomerSet; class CDBSampleDoc; class CCustomerView : public CRecordView { 96 8 Connecting to Data Sources DECLARE_DYNCREATE(CCustomerView) Simpo PDF Merge public: enum { and Split IDD = IDD_CUSTOMER_FORM }; http://www.simpopdf.com Unregistered Version CCustomerSet* m_pSet; public: CCustomerView(); CCustomerSet* GetRecordset(); virtual CRecordset*... members as the COrderView class You can add the following initial code to the CustomerView.cpp: // CCustomerView implementation #include “stdafx.h” #include “resource.h” IMPLEMENT_DYNCREATE(CCustomerView, CRecordView) CCustomerView::CCustomerView(): CRecordView(CCustomerView::IDD), m_pSet(NULL) { } CCustomerSet* CCustomerView::GetRecordset() { ASSERT(m_pSet != NULL); return m_pSet; } CRecordset* CCustomerView::OnGetRecordset()... access to the table The class should then be created with the data members shown as follows: CStringW CStringW CStringW CStringW CStringW CStringW CStringW CStringW CStringW CStringW CStringW m_CustomerID; m_CompanyName; m_ContactName; m_ContactTitle; m_Address; m_City; m_Region; m_PostalCode; m_Country; m_Phone; m_Fax; Don’t forget to comment out the #error directive in the CustomerSet.cpp file Change... Change each of the CStringW types here to CString and then save the class file At this point, you could add a CCustomerSet member to the document so that it is created when the document object is created Right-click the CDBSampleDoc class name in Class View and add a variable of type CCustomerSet with the name m_CustomerSet You can leave the access specifier as public You will find an #include directive... IDD_ORDERS_FORM }; COrderSet* m_pSet; // Inline function definition CDBSampleDoc* GetDocument() const { return reinterpret_cast(m_pDocument); } COrderSet* GetRecordset(); virtual CRecordset* OnGetRecordset(); COrderView(); // constructor now public #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif }; This code is based on the CProductView that... COrderView::AssertValid() const { CRecordView::AssertValid(); } void COrderView::Dump(CDumpContext& dc) const { CRecordView::Dump(dc); } #endif //_DEBUG The DoDataExchange() function links the controls in the dialog to the fields in the recordset The definition of this function is: 95 6 Connecting to Data Sources Simpo PDF Merge void COrderView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX);... you can add the code for the class definition as: #pragma once class COrderSet; class CDBSampleDoc; // Declare the class name // Declare the class name // COrderView form view class COrderView : public CRecordView { DECLARE_DYNCREATE(COrderView) protected: virtual ~COrderView(){} virtual void DoDataExchange(CDataExchange* pDX); virtual void OnInitialUpdate(); // DDX/DDV support public: enum { IDD =... initialized in the constructor; otherwise, add the initialization as in the code that follows You can set the parameter count in the CCustomerSet constructor as follows: CCustomerSet::CCustomerSet(CDatabase* pdb) : CRecordset(pdb) , m_CustomerIDparam(_T””)) { m_CustomerID = _T(“”); 97 2 Connecting to Data Sources Simpo PDF Merge m_CompanyName = _T(“”); m_ContactName = _T(“”); m_ContactTitle = _T(“”);... implement it in CustomerView.cpp as follows: void CCustomerView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) { if(bActivate) { if(!m_pSet->IsOpen()) return; CDBSampleDoc* pDoc = static_cast(GetDocument()); // Set current customer ID as parameter m_pSet->m_CustomerIDparam = pDoc->m_OrderSet.m_OrdersCustomerID; m_pSet->Requery(); // Get data from the DB CRecordView::OnInitialUpdate();... CCustomerView::OnGetRecordset() { return m_pSet; 96 9 Chapter 19 } // COrderView diagnostics Simpo PDF #ifdef _DEBUG Split Unregistered Version - http://www.simpopdf.com Merge and void CCustomerView::AssertValid() const { CRecordView::AssertValid(); } void CCustomerView::Dump(CDumpContext& dc) const { CRecordView::Dump(dc); } #endif //_DEBUG This is the same boilerplate code as in the COrderView class, and the functions . http://www.simpopdf.com You can therefore fill out the code for the DoDataExchange() function in the CProductView class like this: void CProductView::DoDataExchange(CDataExchange* pDX) { CRecordView::DoDataExchange(pDX); DDX_FieldText(pDX,. in DoFieldExchange() CProductSet U ni t s i nStoc k P roduct N ame Pr o d u c t I D m_ProductName m_ProductID m_UnitslnStock DDX calls in DoDataExchange() DDX calls in DoDataExchange() 94 7 Connecting. file, you can add the code for the class definition as: #pragma once class COrderSet; // Declare the class name class CDBSampleDoc; // Declare the class name // COrderView form view class COrderView