Borland Online And The Cobb Group Present:


January, 1995 - Vol. 2 No. 1

Designing view classes

In the accompanying article, "Implementing MVC Designs Using OWL's Doc/View Architecture," we display some of the document data in a view class named TModelView, which we derived from the TView class (the base class for all Doc/View view classes) and from the TListBox class (one of the standard OWL control classes). Instead of doing this, we could have simply created a TListBox pointer as a member of the TModelView class.

At first, this may seem to be a relatively simple alternative (just return the TListBox pointer from the GetWindow() function instead of returning the TModelView object's this pointer, as we did). Unfortunately, using this method creates a number of complications when the view needs to respond to events.

In addition, you'll notice that we didn't use a transfer buffer to initialize the TListBox members or to retrieve their data, as we did in the CARS3.EXE application in the October article. Here, we'll review these design choices and show how you can determine if you should design your TView-derived classes the same way.

Inheriting an interface object

All three Doc/View view classes that Borland provides­­TWindowView, TListView, and TEditView­­derive directly from the TView class and an OWL interface class (something derived from the TWindow class). To create your own view classes, you can either derive from these classes, use multiple inheritance to derive a new class from the TView class and some other interface class, or enclose an interface object pointer in your class.

Obviously, the simplest route is to derive from one of the existing view classes. (In particular you'll want to look at the TWindowView class, since many applications display data in a window.) However, you may want your view to provide behavior other than what these classes provide. (For example, the TListView class assumes you're using it to display and edit the lines of text in a file.)

If you decide to use multiple inheritance to derive your new view class from TView and an OWL interface class, you'll need to do more work than if you'd used one of the existing classes. However, you'll make it easier for the view object to perform event-handling. For example, in the class TModelView, we created four message response functions: VnIsWindow(), VnCommit(), CmSelChange(), and CmDoubleClick().

The first two functions respond to view notifications, and the others respond to the messages that Windows sends when a user selects an item in a list box or double-clicks on an item. Since we derived TModelView from the TListBox class, we were able to define a message response table for the class and respond to these messages from inside the class.

If we had instead decided to include a TListBox object pointer in the TModelView object, we would have had to do one of two things:

For these reasons, you'll probably want to derive your view class from the existing view classes or use the multiple inheritance approach.

Transferring data to a control object

If you decide to derive your view class from one of the standard OWL control classes like TListBox, TGauge, or TSlider, you'll need to consider how you're going to set the initial control data and how you're going to retrieve that data. If you've created complex dialog boxes using OWL, you've probably used the transfer buffer mechanism to set and return these values.

However, the reason for using transfer buffers in a dialog box is that you can't call the member functions of the dialog box's control objects until their associated window elements exist. Unfortunately, these elements don't exist until you call the TDialog object's Execute() member function­­and they're destroyed before the function returns. (To allow a transfer buffer to initialize the controls, the Execute() function creates all the controls and then transfers the data. As the user closes the dialog box, the Execute() function transfers the new data from the controls to the transfer buffer before returning.)

In contrast, you can respond to two important view notification messages when you want to set or retrieve an interface object's data directly. After the TDocManager object successfully creates a view object, it posts the message VN_VIEWOPENED along with a pointer to the view it just created.

By creating a message response function with the signature

void VnViewOpened(TView*);

and entering

EV_VN_VIEWOPENED,

in the view's response table, you can safely initialize the control from inside this function. As an alternative, you can do what we did in the CARS4.EXE application: override the Create( ) member function from the control class and place all your initialization commands after calling the Create( ) function for the base control class.

Similarly, when the TDocManager object is about to close a view, it posts the message VN_VIEWCLOSED along with a pointer to the view it's about to close. You can respond to this message by creating a response table entry and a similar response function that queries the control's values and transfers them back to the document.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.