1 Basic Knowledge
1.1 MFC Document view structure program structure Overview
When we use MFC Appwizard to generate an MFC program, we use all the default settings (of course, also multiple documents. This article mainly discusses the single document based on multiple documents, because the latter and the former have many similarities, but the former is more complex and more commonly used .), Assuming that your program is named A, you will get six classes: cmainframe, cchildframe, caboutdlg, cadoc, caview, and caapp (single document only contains one cchildframe class, and the rest are the same ). The specific meanings of these classes will be given later. Here we first need to provide an MFC support document view structure Program (hereinafter referred to as APP) which is mainly composed:
U an app (corresponding to the caapp class) can contain multiple document templates (cdoctemplate), but MFC Appwizard (either SDI or MDI) only generates one by default. However, in actual development, a document template is insufficient and needs to be manually added (examples are provided in the actual project example below ). This structure is implemented through the cdocmanager * m_pdocmanager member variable of cwinapp in MFC. Our caapp inherits from the cwinapp class provided by MFC.
The U cdocmanager class has a pointer linked list cptrlist m_templatelist to maintain these document templates. These document templates all use adddoctemplate (pdoctemplate) in caapp: initinstance ).
U cdoctemplate has three member variables, which respectively store the cruntimeclass pointer of the document, view, and frame, and also hold the member variable m_nidresource, which is used to specify the menu resources used when the document is displayed. The four data copies are specified in the cdoctemplate constructor in caapp: initinstance. In the document, you have a pointer (m_pdoctemplate) pointing to the cdoctemplate ).
U a document can have multiple views, which are maintained by the cptrlist m_viewlist member variable in the document.
U cframewnd has a member variable cview * m_pactiveview pointing to the current active view.
U cview has a member variable cdocument * m_pdocument pointing to the view-related document.
[Note]: ① by default, the mfc sdi/MDI program generates a document template and adds the document template to the linked list of the document template. This is provided by default by MFC, therefore, this document template will be inserted to the first position of the document template, and MFC can be determined through the specific position of this document template. By default, when we click File (open)/file (new), this document template will be enabled.
In addition to the in-depth analysis listed above in the "in-depth introduction to MFC", Mr. Hou jie should (to a large extent, more importantly) master the following knowledge about mfc sdi/MDI:
U document: This document is used to store data and process data. A document is opened whenever the mfc sdi/MDI responds to file (open)/file (new. The document can have multiple views. The relationship between the document and the view can be understood as follows: The document is the object observed by the view.
U view nature: in windows, a view is a window, that is, a visualized rectangular area. A view is used to represent the data of a document. However, each view must be attached to a frame (SDI is mainframe and MDI is childframe ). Of course, you can create a view and display it on your own.
The essence of the U framework: the framework is actually a Windows window. However, menus, toolbar, and status bar can be placed on the framework. The view is placed in the customer area of the framework. Therefore, the window we see in MFC is actually the result of the combination of frame and view.
U has only one active document, framework, and view in the program at a certain time, that is, the current document, framework, and view.
1.2 mutual access between various mfc sdi/MDI classes
In actual project development, the most commonly used is the mutual access between various classes. Here we will summarize what is mentioned in the network and books, which is also used by the author in actual development.
Access Object
Access location
Access Implementation
Application app
Any location
① Afxgetapp ();
② Add the following to the file where the application app is to be used:
Extern caapp theapp, and then directly use the global theapp variable.
Main Frame window
Any location
① Afxgetmainwnd ();
② Afxgetapp ()-> m_pmainwnd;
View
Framework class
Getactiveview (); // The current activity View
Document class
Getfirstviewposition (); // you can obtain all views.
Getnextview ();
Document
Document class
Getdocument ();
In the template class
Getfirstdocposition (); // This document template corresponds to all documents
Getnextdoc ();
Framework class
Getactivedocument (); // The current activity when
Child Framework (in MDI)
In the main framework class
① Mdigetactive ();
② Getactiveframe ();
View class
Getparentframe ();
Document Template
Document class
Getdoctemplate ();
Application app
Getfirstdoctemplateposition ();
Getnextdoctemplate ();
Note: 1) All the above methods are provided. In actual access, you may need to perform the following simple operations, such as type conversion and loop traversal;
2) You may not list all possible locations for mutual access, but they can be obtained through their combination.
2 association between documents, views, and frameworks
The core of mfc sdi/MDI lies in the association between documents, views, and frameworks, forming an organic and operational whole. MFC provides default associations, but in actual project development, they need to be dynamically associated.
2.1 association between documents and views
When you use the MFC Appwizard to claim the mfc sdi/MDI program, the initinstance () method of the app class contains the following code (assuming that the project name is test ):
U SDI
Csingledoctemplate * pdoctemplate;
Pdoctemplate = new csingledoctemplate (
Idr_mainframe,
Runtime_class (ctestdoc ),
Runtime_class (cmainframe), // main SDI frame window
Runtime_class (ctestview ));
Adddoctemplate (pdoctemplate );
U MDI
Cmultidoctemplate * pdoctemplate;
Pdoctemplate = new cmultidoctemplate (
Idr_testtype,
Runtime_class (ctestdoc ),
Runtime_class (cchildframe), // custom MDI child frame
Runtime_class (ctestview ));
Adddoctemplate (pdoctemplate );
Here we use the constructor cdoctemplate (csingledoctemplate in SDI or cmultidoctemplate in MDI) to integrate the document, view, and framework (SDI and main framework, MDI and self-Framework) the Association forms a whole.
Manually associate text boxes with views
In actual project development, it is not enough to rely solely on the text generated by the MFC Appwizard and the view and framework. Therefore, we need to master the manual Association. There are two ways to manually associate a document with a View:
L simulates the MFC Appwizard implementation and uses the cdoctemplate constructor:
In the analysis, we can see that through the cdoctemplate (csingledoctemplate in SDI or cmultidoctemplate in MDI) constructor we can get the association of documents, views and frameworks. Therefore, you can simulate this method for association. The specific implementation method is as follows:
1) create a new document, view, and framework class by using the insert MFC class in VC
. Note that the framework class uses javasichildwnd as the base class, the document class uses cdocument as the base class, and the View class can select cview or its subclass (ceditview) as the base class as needed.
2) Add menu resources for the framework by adding a new dish under the menu in the VC resource window
Single, of course, you can copy VC to provide the default menu for modification.
3) Add the following code in initinstance () of the app class:
Cmultidoctemplate * m_pdoctemplate;
M_pdoctemplate = new cmultidoctemplate (
Idr_testtype, // change it to the ID of the menu resource you created
Runtime_class (ctestdoc), // change it to your new document class
Runtime_class (cchildframe), // change it to the framework class you created
Runtime_class (ctestview); // change it to The View class you created
Adddoctemplate (m_pdoctemplate );
4) to record this document template, you can add a cmultidoctemplate * to the app class *
Type Variable to maintain this document template.
L The constructor of cdoctemplate is used to Associate Documents, views, and frameworks. However
Sometimes we don't want to create a new document template. We just want to provide different results for the same data, or add a new view for the same document, and provides a switchover between them. Another possibility is that we are not a program supported by the document view structure. We want to add a document for the view to better separate the business logic from the presentation layer. The implementation method of the first method:
Step 1: Use VC 6.0 to create a project named multiview. In addition to selecting single-document attributes, the "default" method is used for all items. You can obtain the following classes: cmainframe, cmultiviewapp, cmultiviewdoc, cmultiviewview, and caboutdlg;
Step 2: Create a New View and add a new MFC class (insert-> New Class). The base class is cview (or a derived subclass of cview, such as ceditview ). The class name is canotherview, which is the new view. Add the getdocument implementation for canotherview:
Cmultiviewdoc * canotherview: getdocument ()
{
Return (cmultiviewdoc *) m_pdocument;
}
Step 3: Add member variable records to the two views in cmultiviewapp:
PRIVATE:
Cview * m_pfirstview;
Cview * m_panotherview;
Add a menu item "View" to the program menu idr_mainframe. This menu item has two sub-Menus "View 1" and "View 2" and adds the corresponding function (void cmultiviewapp: onshowfirstview () and void cmultiviewapp: onshowsecondview ());
Step 4: Create a New View: add the code in bool cmultiviewapp: initinstance:
.......
// Create a new view
Cview * m_pactiveview = (cframewnd *) m_pmainwnd)-> getactiveview ();
M_pfirstview = m_pactiveview;
M_panotherview = new canotherview ();
// Associate Documents with views
Cdocument * m_pdoc = (cframewnd *) m_pmainwnd)-> getactivedocument ();
Ccreatecontext context;
Context. m_pcurrentdoc = m_pdoc;
// Create a view
Uint m_idforanotherview = afx_idw_pane_first + 1;
Crect rect;
M_panotherview-> Create (null, null, ws_child, rect, m_pmainwnd,
M_idforanotherview, & context );
......
Step 5: You have created a view and associated it with the document. What we need to do now is the conversion between views. Add the implementation code in void cmultiviewapp: onshowfirstview:
Void cmultiviewapp: onshowfirstview ()
{
// Todo: add your command handler code here
Uint temp =: getwindowlong (m_panotherview-> m_hwnd, gwl_id );
: Setwindowlong (m_panotherview-> m_hwnd, gwl_id,: getwindowlong (m_pfirstview-> m_hwnd, gwl_id ));
: Setwindowlong (m_pfirstview-> m_hwnd, gwl_id, temp );
M_panotherview-> showwindow (sw_hide );
M_pfirstview-> showwindow (sw_show );
(Cframewnd *) m_pmainwnd)-> setactiveview (m_pfirstview );
(Cframewnd *) m_pmainwnd)-> recalclayout ();
M_pfirstview-> invalidate ();
}
Add the implementation code in void cmultiviewapp: onshowsecondview:
Void cmultiviewapp: onshowsecondview ()
{
// Todo: add your command handler code here
Uint temp =: getwindowlong (m_panotherview-> m_hwnd, gwl_id );
: Setwindowlong (m_panotherview-> m_hwnd, gwl_id,: getwindowlong (m_pfirstview-> m_hwnd, gwl_id ));
: Setwindowlong (m_pfirstview-> m_hwnd, gwl_id, temp );
M_pfirstview-> showwindow (sw_hide );
M_panotherview-> showwindow (sw_show );
(Cframewnd *) m_pmainwnd)-> setactiveview (m_panotherview );
(Cframewnd *) m_pmainwnd)-> recalclayout ();
M_panotherview-> invalidate ();
}
Step 6: For demonstration, here we will mark different views and add the following code in the ondraw method of cmultiviewview and canotherview respectively:
PDC-> textout (400,300, "first view ");
PDC-> textout (400,320, pdoc-> gettitle ());
And
PDC-> textout (400,300, "another view ");
PDC-> textout (400,320, pdoc-> gettitle ());
This is all done, but there are several notes in the implementation process:
1) Because related classes are used in the implementation, You need to include relevant header files where necessary. This is omitted. The default constructor of canotherview is protected and must be changed to public, you can also provide a method for generating canotherview objects (because you want to create a view object );
2) Here is a sample code. In actual development, you can obtain the specific application you want to implement through reference implementation (for example, different view classes and different quantities, more importantly, different implementations of business logic );
The second method for associating views and documents: we use the ccreatecontext class to associate them. The specific implementation is as follows:
M_panotherview = new canotherview (); // a new view, which can be changed to the view you created
// Obtain an existing document, which can be the document you created
Cdocument * m_pdoc = (cframewnd *) m_pmainwnd)-> getactivedocument ();
// Associate Documents with views
Ccreatecontext context;
Context. m_pcurrentdoc = m_pdoc;
// Create a view
Uint m_idforanotherview = afx_idw_pane_first + 1; // ID of the created view, which can be set by yourself
Crect rect;
M_panotherview-> Create (null, null, ws_child, rect, m_pmainwnd, m_idforanotherview, & context );
L set the relationship between the framework and view. For details, see the association between the framework and view.
2.2 association between frameworks and views
In the first part of the analysis, we know that the framework and view are both windows Windows, but the framework provides resources such as menus, title bars, and status bars, while the view is just a rectangular area. The Visual View in the MFC program determines that most of the time it is attached to a framework (the mainframe in SDI and the child frame window in MDI, the framework is equivalent to a window container (of course, it is also a Windows window), and the view is placed in the customer area of the framework.
The association between the framework and view can also be achieved by imitating the MFC Appwizard and using the cdoctemplate constructor. That is, the association between the framework and view is the same as that between the document and view in 2.1, see the detailed implementation above.
As mentioned above, in many cases, we do not need to provide a new document template. We just need to display a new window (MDI program ), for example, when we manage the MIS system interface, a processing window is usually displayed when you click a menu option. To display a new window, we can use the opendocumentfile () method of cdoctemplate to open a document implementation, so as to establish a set of documents, views, and frameworks. As we have analyzed above, we can see that the window of MFC is actually a combination of the framework and view. We do not have to provide the entire system of documents, views, and frameworks, we only need to combine the framework and view to display the window. This requires two steps: the first step is to associate the view with the Framework, step 2: display the frame (that is, display in a Windows window ). The following describes how to associate a framework with a View:
Cchildframe * pfrm = new cchildframe (); // The framework can be a new or custom framework class.
Ccreatecontext context;
Context. m_pnewviewclass = runtime_class (cdemoview); // The view can be the view you want to display
Pfrm-> loadframe (idr_test2type, ws_child
Ws_overlappedwindow, this, & context); // you can modify menu resources.
Pfrm-> showwindow (sw_show); // display window
Pfrm-> initialupdateframe (null, true); // call the oninitialupdate () and activeframe () of the view. You can set the title of the window here.
Of course, you can add the associations between views and documents here. The specific implementation is to add the following code:
Context. m_pcurrentdoc = m_pdoc; // m_pdoc is the object to be associated.
3. code example
This section takes the interface design and development of a general MDI project as an example, and attaches the above analysis to the practices.
3.1 Scenario Description
A common interface logic is that a user opens a system and displays basic menus for user logon, logout, and user management (of course, this can also be achieved through a user logon dialog box ). After you log on to the console correctly, the system's function operation interface is displayed. When you click a menu item (corresponding to one or more business logics), a processing operation interface (not a dialog box) is displayed ). Each functional operation interface can coexist in a framework and can be maximized, minimized, or disabled.
3.2 code implementation
Next we will use the technology mentioned above to provide detailed implementation solutions.
Step 1: Create an MFC project named "Demo", and select not supported by document/view architecture (remove the default check box in step 2 ). By default, the system generates five classes: cdemoapp, cmainframe, cchildframe, caboutdlg, and cdemoview. The meaning of each class has been analyzed above. In addition, the system provides two default menus: idr_demotype and idr_mainframe, which change the "file" of the menu of idr_mainframe to "start" (it seems more professional and should not be empty, then, change the sub-menu of this menu item to "login" and "logout", use the default sub-menu, and change the ID numbers of the first two: "id_login" and "id_logout ". Copy (CTRL + C) and paste (CTRL + V) to obtain a menu resource named idr_mainframe1. Delete the original idr_demotype menu (Save the name first ), change the idr_mainframe1 name to idr_demotype. Add a menu item "function" for idr_demotype, and add two sub-menu items "business logic 1" and "business logic 2". The ID numbers are id_func_one and id_func_two, respectively.
Step 2: Add two variables to the cdemoapp and save the menu resources:
Hmenu m_hopmenu;
Hmenu m_hinitmenu;
Add the code in bool cdemoapp: initinstance:
M_hinitmenu =: loadmenu (hinst, makeintresource (idr_mainframe ));
M_hopmenu =: loadmenu (hinst, makeintresource (idr_demotype ));
To display the effect, add the code in bool cdemoapp: initinstance:
Pframe-> setwindowtext ("not logged on ");
M_ncmdshow = sw_showmaximized;
Pframe-> showwindow (m_ncmdshow); // provided by the system
Pframe-> updatewindow ();
Add a response function for the "Log on" menu item (use Class Wizard and select cdemoapp for class name)
Void cdemoapp: onlogin ()
{
// Todo: add your command handler code here
Setmenu (afxgetapp ()-> m_pmainwnd-> m_hwnd, m_hopmenu );
Afxgetapp ()-> m_pmainwnd-> setwindowtext ("logged on ");
}
Similarly, add a response function for the "logout" menu item:
Void cdemoapp: onlogout ()
{
// Todo: add your command handler code here
If (cmainframe *) afxgetmainwnd ()-> m_pfunconeframe! = NULL)
(Cmainframe *) afxgetmainwnd ()-> m_pfunconeframe-> sendmessage (wm_close );
If (cmainframe *) afxgetmainwnd ()-> m_pfunctwoframe! = NULL)
(Cmainframe *) afxgetmainwnd ()-> m_pfunctwoframe-> sendmessage (wm_close );
Setmenu (afxgetapp ()-> m_pmainwnd-> m_hwnd, m_hinitmenu );
Afxgetapp ()-> m_pmainwnd-> setwindowtext ("not logged on ");
}
This achieves the login and logout functions (of course, there may be a dialog box to verify the user's permissions and legitimacy, as shown in the following figure ), the user operation menu changes during logon and logout. Note: the first two lines of code in onlogout must be closed and added when you log out. For definitions and functions of m_pfunconeframe and m_pfunctwoframe, see the following definitions.
Step 3: Add a new sub-framework class cdemoframe, whose base class is cmdichildwnd. Add two new view classes cfunconeview and cfunctwoview. The base class of the former is cview, and the latter is cformview. Of course, to add the cfunctwoview class, you must first insert a dialog box resource, change the ID to idd_func_two_dlg, and change the attribute style to "child" (popup by default ). In this way, the dialogid is idd_func_two_dlg when the cfunctwoview is created. Change the cdemoframe constructor to public (protected by default ).
Step 4: Add two member variable records corresponding to each business logic window to cmainframe, and add in mainfrm. h:
Cdemoframe * m_pfunconeframe;
Cdemoframe * m_pfunctwoframe;
And initialize in cmainframe: cmainframe:
Cmainframe: cmainframe ()
{
// Todo: Add member initialization code here
M_pfunconeframe = NULL;
M_pfunctwoframe = NULL;
}
Step 5: Add a response function for "business logic 1" (In cmianframe ):
Void cmainframe: onfuncone ()
{
// Todo: add your command handler code here
If (m_pfunconeframe! = NULL)
{
M_pfunconeframe-> mdiactivate ();
Return;
}
M_pfunconeframe = new cdemoframe ();
Ccreatecontext context;
Context. m_pnewviewclass = runtime_class (cfunconeview );
M_pfunconeframe-> loadframe (idr_mainframe, ws_maximizews_overlappedwindow, this, & context );
M_pfunconeframe-> setwindowtext ("business logic 1 ");
M_pfunconeframe-> showwindow (sw_showmaximized );
M_pfunconeframe-> initialupdateframe (null, true );
}
Same as above, add a response function for "business logic 2:
Void cmainframe: onfunctwo ()
{
// Todo: add your command handler code here
If (m_pfunctwoframe! = NULL)
{
M_pfunctwoframe-> mdiactivate ();
Return;
}
M_pfunctwoframe = new cdemoframe ();
Ccreatecontext context;
Context. m_pnewviewclass = runtime_class (cfunctwoview );
M_pfunctwoframe-> loadframe (idr_mainframe, ws_maximizews_overlappedwindow, this, & context );
M_pfunctwoframe-> setwindowtext ("business logic 2 ");
M_pfunctwoframe-> showwindow (sw_showmaximized );
M_pfunctwoframe-> initialupdateframe (null, true );
}
In this way, the above requirements are basically met, but it should be noted that:
1) You need to add the appropriate header file in the appropriate place, that is, you need to include the header file implemented by the class.
2) The above is actually a lot of general interface operation templates for MIS Management Systems. You can make corresponding modifications in the actual project development (mainly to implement the corresponding business logic );
3) The default main frame window provided here is relatively simple (default );
4) in the above example, the system generates a program that does not support the MFC Document/view architecture. If the default program that supports the MFC Document/view architecture already exists, copy the above implementation in the app.
5) You can use the cchildframe class generated by MFC Appwizard to replace the cdemoframe class. However, we recommend that you create a new framework class. You can use different framework classes for business logic 1 and business logic 2 to simulate the implementation.
Of course, you may need to add documents for the view to achieve loose coupling between the business logic and the presentation layer. The following describes how to add document view structure support for cfunconeview.
Step 6: Add a document class cdemodoc. The base class is cdocument and change the cdemodoc constructor to public (protected by default). For demonstration, add the function getdata () for cdemodoc ():
Cstring cdemodoc: getdata ()
{
Return "Hello World ";
}
Step 7: add the function getdocument for cfunconeview:
Cdemodoc * cfunconeview: getdocument ()
{
Return (cdemodoc *) m_pdocument;
}
And modify the ondraw function:
Void cfunconeview: ondraw (CDC * PDC)
{
// Cdocument * pdoc = getdocument ();
// Todo: Add draw code here
Cdemodoc * pdoc = getdocument ();
PDC-> textout (50, 50, pdoc-> getdata ());
}
Step 8: Associate cdemodoc with cfunctwoview: Modify void cmainframe: onfuncone () function:
Void cmainframe: onfuncone ()
{
// Todo: add your command handler code here
If (m_pfunconeframe! = NULL)
{
M_pfunconeframe-> mdiactivate ();
Return;
}
M_pfunconeframe = new cdemoframe ();
Cdemodoc * m_pdoc = new cdemodoc ();
Ccreatecontext context;
Context. m_pnewviewclass = runtime_class (cfunconeview );
Context. m_pcurrentdoc = m_pdoc;
M_pfunconeframe-> loadframe (idr_mainframe, ws_maximizews_overlappedwindow, this, & context );
M_pfunconeframe-> setwindowtext ("business logic 1 ");
M_pfunconeframe-> showwindow (sw_showmaximized );
M_pfunconeframe-> initialupdateframe (null, true );
}
This completes the entire process. Note:
1) You need to add the appropriate header file in the appropriate place, that is, you need to include the header file implemented by the class.
2) If you have programs supported by the document view, you can use the documents provided in the system;
The above provides a simple and common interface operation implementation. You can refer to the implementation, such as adding more views and implementing your own business logic.