Version 1.3

Made construction methods more compatible with CPropertySheet in order to permit arrays of pages

Added demo project for SDI application

Version 1.2

This version includes the following new features and fixes:

Made CPropertyView::PreCreateTabControl virtual

Fixed a potential crash in CPropertyView::OnSize

Improved page name handling (more methods)

Made the base class of CPropertyView customizable

Version 1.1

This version includes the following new features and fixes:

Support for scrolling.

Drawing and sizing stuff now works correctly if the tab control works with tabs at bottom (TCS_BOTTOM).

The message filter doesn't kill some important keystrokes (like CTRL+V) any more.

Introduction

Sometimes it may be very useful to organize the data of your document in a view that behaves like a property sheet. While writing an application that permits a user to modify the properties of very complex objects, I created a generic CView-derived class that permits, to create with very little work, a "Property Sheet"-like representation of the data inside a view.

In my implementation of a "Property Sheet"-like view, I have tried to reflect the interface of the standard classes CPropertySheet and CPropertyPage, in order to keep the usage of those classes very simple. This view behaves like a property sheet, providing also support for Ctrl+TAB, Shift+Ctrl+Tab and all typical keyboard combinations to switch between the pages.

Although they are very similar to their examples, there are some slight differences in the usage of some methods. These differences are based mainly upon the different data handling concepts between a dialog and a view: in a modal dialog, the data is edited in a modal window that will be closed by a specific action of the user (e.g., clicking OK or Cancel). On the other hand, a view is a living representation of the data stored in a document. While modifying the data in the view, the document is also modified and the framework permits binding of more than one view to a document. An optimal implementation of a view either updates itself if it receives the signal that the document content was changed by another view, or signals the change to the other views when the user modifies the data.

Usage

First of all, create a dialog template for each page of your view, like if you were creating pages for a property sheet. As I have tested it, there is no limitation in the type of controls that you can place on your page. As in a property page, the caption of each dialog template will become the text of the corresponding tab. Feel free to use accelerator mnemonics in the static members, since they will work correctly.

Use Class Wizard to create a class derived from CDialog for each dialog template. Now we need to replace some things in our source code:

Derive your view from CPropertyView instead of CView

Derive your dialogs from CPropertyViewPage instead of CDialog

It is a very good idea to do this by a case sensitive search and replace, since in this manner also, all special inline comments for Class View and Class Wizard will be correctly updated.

Now exit your workspace, delete the .ncb and .clw files of your project, reopen the workspace and regenerate the .clw file by invoking Class Wizard. By default, each automatically-generated view contains an implementation for the methods OnDraw and PreCreateWindow. You can safely remove them both from your view class.

Typically, a property sheet defines its pages as member variables and calls the AddPage method in its constructor. The property sheet then creates the pages only if the user switches on a page by selecting the corresponding tab. This is one of the considerable differences in my CPropertyView implementation: by calling AddPage, the corresponding page is immediately created. This means that AddPage can only be called if the view still exists. A good place to do this is in the OnCreate method of the view:

In this example, you can see another slight difference between CPropertySheet and CPropertyView: The method SetImageList is used to specify an image list for the tab icons and the method AddPage can contain an additional parameter specifying the index of the used image from the image list.

CPropertyView implements nearly all methods of CPropertySheet except for these:

Construct (not needed)

SetTitle (it makes no sense in a view)

SetFinishText (it also makes no sense in a view since there are no OK, Apply or Cancel buttons)

SetWizardButtons (see above)

SetWizardMode (see above)

DoModal (see above)

EndDialog (see above)

No other work has to be done in the view class, since all typical overrides for a view are implemented in CPropertyView and they are passed as needed to the specific page classes.

Our pages are implemented as classes derived from CPropertyViewPage. This class is nearly identical in its interface to CPropertyPage with a few slight differences. Also here, some methods are not available:

Construct (not needed)

CancelToClose (Since there are no buttons, it makes no sense)

QuerySiblings (I have never used it. If needed, I will implement it)

OnCancel (There is no such situation)

OnReset (see above)

OnQueryCancel (see above)

OnWizardBack (see above)

OnWizardNext (see above)

OnWizardFinish (see above)

Unlike a property sheet, OnInitDialog is not the right place to initialize the controls in your page with the contents. Override OnInitDialog only if you need to configure your controls on a one-time basis like specifying an image list, setting a font or filling a combo box with a fixed number of choices (the data in your document is the index of the selected choice, not the choices themselves).

For this purpose, CPropertyViewPage implements also all typical functions of a view, like OnInitialUpdate and OnUpdate. After the view (and all its pages) are successfully created, the OnInitialUpdate function is also called for each existing page. The default implementation calls OnUpdate(NULL, 0, NULL) like in CView. If you wish to support dynamic update between different views of your document, you also need to implement OnUpdate and obviously call UpdateAllViews on each modification of the document.

Here is an example on how to initialize the controls with the contents of the document:

When saving the document, we also need a method that permits saving changed data in the window controls to the document. Saving a document is somewhat similar to clicking the "Apply" button in a property sheet. In each page, the OnApply method should be implemented to copy the data back to the document:

If you support more than one view per document in your application, you must obviously implement quite a different approach. You must call Apply either when a property view loses the focus (in order to update the document) or in OnSaveDocument for the last view that has the focus.

More Advanced Usage

The second example shows a more advanced usage based on the very nice resizable dialog classes programmed by Hans Bühler. Since the use of a modified dialog class is very probable, the base class of CPropertyViewPage is the define baseCPropertyViewPage set by default to CDialog. This has the advantage that you can easily customize your CPropertyViewPage with your favorite dialog-based class. To change the base class of CPropertyViewPage, you simply need to redefine baseCPropertyViewPage. A good place to do this is in StdAfx.h:

The resizable dialog class included in the second sample is neither the last version of Hans Bühler's classes nor the original version. I had to make some modifications since his cdxCSizingDialog did not implement all constructors of CDialog. The last version of these classes I found had some more features and a slightly modified interface. I did not have the time to test it in conjunction with my property view classes, but someone told me that it did not work correctly. In any case, the included version works very well for me, so I felt no desire to update to the latest version. You can find the original classes in the article Dynamic child window positioning.

Essential Reference

Since the usage is very similar to a standard property sheet and a standard view, I will document only some methods specific to these classes.

Override this function if you wish to modify the style of the tab control.

virtualvoid CPropertyView::SetModified (BOOL bChanged = TRUE)

Override this function to provide special handling in case of modification. The default implementation calls GetDocument()->SetModifiedFlag(bChanged).

CImageList* CPropertyView::GetImageList() const;

This function returns a pointer to the image list associated with the tab control.

CImageList* CPropertyView::SetImageList (CImageList *pImageList)

This function sets an image list for the tab control.

void CPropertyView::EnableScrollView (BOOL bScrollView);

By default, the property view enables dynamic scroll bars that appear if the view is sized smaller than needed to display the active property page. To disable or enable this feature, use this function.

You can override these two functions to provide your own implementation of modification handling. The default implementation calls CPropertyView::SetModified (bModified) or returns the current modification state.

Share

About the Author

I started programming computers in 1979. After years of management and administration application programming, I started to work mainly in the telecommunication sector in 1986. While working at Cycos AG (formerly known as PP-COM GmbH) from 1992 to 1999 I was involved in the development of core and GUI components of their telematic server MRS for ISDN. After that I worked for a telecommunication company named Infitel International N.V. that produces software for open telecommunication services. After a long stop at Cycos AG, I worked for several other companies like Siemens Enterprise Communications, Axxom Software AG and PSI Logistics GmbH.

If you are curious about my other Computer related activities and previous projects, visit YeaSoft International

Comments and Discussions

I am developing an SDI application using Property Sheet View framework. I wish to use CDocument derived class to read/write data and wish to use Ptoperty View Pages to display the data from the Document. I created SEPARATE classes for each Property View Page (i.e., the pages are not coded in a single cpp file). Of course each Property View Page has a few controls (Edit boxes, Buttons, etc.)

The problems I face are:

1. If I add any function now in the Document derived class, the compiler gives errors 'local function definitions are illegal'. I checked braces '{ and }', brackets, etc by automatic means also!

2. If I fire a NEW project to keep the Property View Pages code in a single .cpp file, then it is becoming difficult to add message maps (such as 'OnSaveData'.

I believe it is better to reframe the Property Sheet View code such that the View Pages (dialog boxes) are kept in separate .h and .cpp files. I request you to give a solution to the problems.