Rogue Wave banner
Previous fileTop of DocumentContentsIndex pageNext file
Objective Grid User's Guide
Rogue Wave web site:  Home Page  |  Main Documentation Page

12.4 Objective Grid Control Architecture

Objective Grid consists of several cells that display information in the grid window. Each cell is associated with a cell type object. Some examples of cell types are edit box, static text, combo box, list box, radio button, push button and check box. As we mentioned earlier, in Objective Grid, each cell type is implemented through a cell type class derived from CGXControl. CGXControl is an abstract base class that defines the interface between a grid object and a cell type object. Responsibilities of the cell type object are drawing the cell and interpreting user input. Additional interfaces in the control class support find/replace and copy/paste.

MFC already provides classes that wrap many Windows controls and we wanted to reuse these classes as cell types in Objective Grid. For example, the CEdit class could be used as edit field, the CComboBox class as combo box cell type. In general, a programmer can very easily inherit from these window classes. However, with Objective Grid the problem was that these window controls all have a very different interface. Therefore we had to come up with ways in which we could use these controls from the grid in a consistent manner adapting each to our requirement.

The solution to this problem was to adapt the interface of the window class to the CGXControl interface. We did this in two ways:

  1. By multiply inheriting from the CGXControl interface and the window class.

  2. By composing a CWnd instance within a wrapper control. Since CWnd is a consistent interface that is implemented (or rather available) on all windows controls (For those ActiveX people, 'Windowed', Windows controls).

Figure 115 shows the resulting class hierarchy for controls in Objective Grid. The implementation of complex controls like CListBox, CEdit, CComboBox and CRichEditCtrl could be reused by multiply inheriting from CGXControl and the window class. You can also observe that primitive controls like push button or check box are implemented directly in Objective Grid.

Figure 115: Control classes hierarchy

12.4.1 The pure CGXControl approach

The grid control classes, CGXEditControl, CGXListBox, CGXComboBoxWnd and CGXRichEditCtrl (among several others) use multiple inheritance to adapt the CWnd interface to the CGXControl interface. Let us take CGXEditControl as an example. The implementation of the other controls is very similar.

Figure 116: CGXEditControl implementation

CGXEditControl is not only an adapter that adapts the functionality of the CEdit class to the grid, it also provides a lot of functionality not provided by the CEdit control class. For example, CGXEditControl is responsible for hiding, showing and setting the focus to the cell, changing the text in the window control and the implementation of Find/Replace, Cut/Paste and text formatting.

The nice part is that the editing functionality could be reused from the CEdit class. Once the focus is set to the edit window, the windows edit control lets the user scroll and edit the text, move the caret, copy/paste text.

There are two types of interfaces in the CGXControl class. Some functions are called from the grid window (e.g. Init(), Draw()) and their implementations will translate the call to the edit window, others will be called from within the control and translate the call to the grid object (e.g. Store(), OnValidate(). Therefore the CGXControl object needs a pointer to the grid object.

Let us look at some important interfaces that implement the core of this control.

12.4.1.1 Functions that are called from the grid and translate into calls to the edit window

virtual void 
Init(ROWCOL nRow, ROWCOL nCol);
virtual void 
Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, 
     const CGXStyle& style, const CGXStyle* pStandardStyle);
virtual BOOL 
GetValue(CString& strResult);
virtual void 
SetValue(LPCTSTR pszRawValue);
virtual BOOL 
LButtonDown(UINT nFlags, CPoint pt, UINT nHitState);
virtual BOOL 
KeyPressed(UINT nMessage, UINT nChar, 
                        UINT nRepCnt = 1, UINT flags = 0);

12.4.1.2 Functions called from within the control which translate the call to the grid object

virtual BOOL 
Store();
virtual BOOL 
OnValidate();
virtual CRect 
GetCellRect(ROWCOL nRow, ROWCOL nCol);

Other interfaces in the CGXControl class handle special events in the grid and provide feedback about changes in the control (e.g. OnModifyCell will be called when the user modifies text).

12.4.2 The Window Wrapper Approach

This approach is used by the CGXWndWrapper class. This class can be used to host any CWnd-based object in the grid. CGXWndWrapper relies on object composition.

Figure 117: CGXWndWrapper implementation

CGXWndWrapper is a kind of "one-way" adapter in opposite to the implementation of class adapters like CGXEditControl. Only interfaces that are called from the grid window (e.g. Init(), Draw()) could be implemented because the CWnd object (the composed object) does not know anything about the grid object. This has the advantage that any kind of windows control can be used through the CGXWndWrapper class but has the disadvantage that several limitations arise because the CWnd cannot send feedback about its state or changes to the grid (except through the limited CWnd interface).

Another disadvantage is that there is no way of transferring data between the grid and the contained object because the CWnd class does not provide a consistent interface (the manipulation interfaces are specific to the control) which lets the grid exchange data with the control. But this problem could be easily solved by providing special CGXWndWrapper derivatives that know how to transfer data with special kinds of Windows controls (e.g. a CGXListBoxWrapper). We do this in the grid to adapt ActiveX controls to the grid.

As there is no way to get feedback from the contained object, the only important interface for the CGXWndWrapper class is:

virtual void 
Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, 
     const CGXStyle& style, const CGXStyle* pStandardStyle);

We have illustrated both approaches that were used in creating grid controls. The pure CGXControl (both multiple inheritance and plain derivation from CGXControl) is preferred over the object wrapper (CGXWndWrapper approach). Some of the issues that relate to the use of these discrete approaches are listed below.

  1. The pure CGXControl approach allows us to have special adaptation for the particular controls. This essentially enables data transfer to and from the control. This results in the possibility that one control object can be reused between several cells. In fact this is used in the grid and is the basis for Objective Grid's control sharing architecture that is explained later in this chapter.

  2. While it is often easy to port a control into the grid with the window wrapper approach it is often more difficult to maintain the control (besides this being resource intensive as mentioned above)

There are some situations when the window wrapper method may be the only usable (or more usable) method. In general this is the case with controls that implement special functionality that they retain as part of their internal state. For example take the case of ActiveX control containers that are used as controls. To have a system to serialize and de-serialize the data that is associated with these and to retain this in the grid would be more work that to use window wrapper and single instance per cell approach. This is also the more easily workable approach with ActiveX controls.



Previous fileTop of DocumentContentsNo linkNext file

Copyright © Rogue Wave Software, Inc. All Rights Reserved.

The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.