Escolar Documentos
Profissional Documentos
Cultura Documentos
www.roguewave.com
WHITE PAPER
Objective Toolkit Filling the Holes in MFC to Provide Todays Modern GUI Features
Executive Summary ...................................................................................................................................... 6 The Development Challenge .................................................................................................................. 6 The Stingray Studio Solution.................................................................................................................. 6 Overview of Objective Toolkit Features ................................................................................................. 6 Introduction ................................................................................................................................................... 9 Quick Tour of Objective Toolkit ................................................................................................................. 10 Features ................................................................................................................................................. 10 Compatibility .........................................................................................................................................11 Complete Online Help System ..............................................................................................................11 Comparing MFC Extensions to Other Component Architectures ...............................................................11 MFC Extensions ....................................................................................................................................11 OLE/ActiveX Controls (OCXs) ............................................................................................................11 DLL Components.................................................................................................................................. 13 Selecting a Component Architecture .................................................................................................... 13 Technical Overview of Objective Toolkit Components .............................................................................. 13 Objective Toolkit Window/Control Components ................................................................................. 13 Button Control ............................................................................................................................... 14 Color Well Control ......................................................................................................................... 15 Masked Edit Control ...................................................................................................................... 16 Browse Edit Control ...................................................................................................................... 16 Marquee Control ............................................................................................................................ 17 Progress Control ............................................................................................................................ 17 Editable List Box Control .............................................................................................................. 17 Tab Control/Tabbed window Components .................................................................................... 18 Calculator Edit Control .................................................................................................................. 20 Calendar Control ............................................................................................................................ 21 Currency Edit Control .................................................................................................................... 21 Customizable Status Bar ................................................................................................................ 22 Customizable Toolbar .................................................................................................................... 23 Shortcut Bar Control ...................................................................................................................... 24 List Bar Control ............................................................................................................................. 25 Date/time Edit Control ................................................................................................................... 25 Extended Win32 Tree Control ....................................................................................................... 26 Objective Toolkit User Interface Extensions ........................................................................................ 28 The Workspace Manager ............................................................................................................... 28 Keyboard Shortcut Components .................................................................................................... 30
www.roguewave.com
WHITE PAPER
User Tools Menu Components....................................................................................................... 30 Thumbnail Components ................................................................................................................. 31 Extended Control Bar Architecture ...................................................................................................... 33 Objective Toolkit MDI Alternatives and Enhancements ...................................................................... 35 Multiple Top-level Interface (MTI) ............................................................................................... 35 Floating Document Interface (FDI) ............................................................................................... 36 Workbook Document Interface (WDI) .......................................................................................... 37 Using MTI, FDI, and WDI ............................................................................................................ 38 The Gradient Caption Extension.................................................................................................... 39 View Components ................................................................................................................................. 39 Image Components ............................................................................................................................... 40 Image Example .............................................................................................................................. 42 Objective Toolkit Utility Components .................................................................................................. 42 Non-User-Interface Utility Extensions .......................................................................................... 42 Miscellaneous User Interface Utility Extensions........................................................................... 43 Challenges of Docking a CView........................................................................................................... 45 Docking Views: Architectural Overview .............................................................................................. 46 Using the Docking Views Architecture ................................................................................................ 47 Layout Manager Framework....................................................................................................................... 47 The Problem.......................................................................................................................................... 48 The Solution.......................................................................................................................................... 48 Why use the Layout Manager? ...................................................................................................... 48 Layout ManagerArchitecture ......................................................................................................... 49 Layout managers provided by Objective Toolkit........................................................................... 53 Implementing custom layout managers ......................................................................................... 53 Adding layout management to your applications .......................................................................... 54 Advanced Docking Windows ..................................................................................................................... 54 Introduction to a new architecture ........................................................................................................ 54 Why this architecture? ................................................................................................................... 55 ActiveScript hosting engine ........................................................................................................................ 59 JavaScript .............................................................................................................................................. 60 VBScript ............................................................................................................................................... 60 Hosting an ActiveScript ........................................................................................................................ 60 ActiveScript classes ....................................................................................................................... 60 The ActiveForm scripting and editing framework................................................................................ 62 ActiveForm classes ........................................................................................................................ 62 The Model-View-Controller (MVC) Framework ....................................................................................... 64 Introduction to MVC ............................................................................................................................ 64 Considering an alternative to DVA ....................................................................................................... 64
www.roguewave.com
WHITE PAPER
Problems with DVA .............................................................................................................................. 65 DVAs architecture is vague ........................................................................................................... 65 DVAs approach is monolithic ....................................................................................................... 66 CViews inhibit reuse ...................................................................................................................... 66 CView/CDialog are incompatible .................................................................................................. 67 OnUpdate creates problems ........................................................................................................... 68 DVAs presentation tied to control ................................................................................................. 68 Overview of the MVC architecture ...................................................................................................... 68 MvcModel ...................................................................................................................................... 69 MvcViewport ................................................................................................................................. 70 MvcController ................................................................................................................................ 70 Connecting the model, viewport, and controller............................................................................ 71 Additional reading on MVC .......................................................................................................... 71 The MVC class hierarchy ..................................................................................................................... 72 MvcVisualComponent ................................................................................................................... 72 MvcVisualPart ............................................................................................................................... 73 MvcLogicalPart ............................................................................................................................. 73 MvcViewport ................................................................................................................................. 73 MVCWrapper_T ............................................................................................................................ 74 IComposite_T ................................................................................................................................ 75 IMvcMsgHandler ........................................................................................................................... 75 The IMvcSubject, IMvcObserver and IMvcMessage Interfaces ................................................... 75 MvcModel ...................................................................................................................................... 76 The MvcPresentationModel_T class.............................................................................................. 76 IMvcVirtualPart ............................................................................................................................. 77 MvcTransactionModel ................................................................................................................... 78 MvcController ................................................................................................................................ 78 The collection classes .................................................................................................................... 79 MVCs Undo/Redo architecture ........................................................................................................... 80 MvcCommand ............................................................................................................................... 80 The transaction model .................................................................................................................... 80 The IMvcUndoRedo interface ....................................................................................................... 81 Tying Undo/Redo into the MVC framework ................................................................................. 81 Solving real problems with MVC ......................................................................................................... 81 Clarifying the role of the model and the viewport ......................................................................... 81 Leveraging the power and flexibility of the subject/observer interfaces ....................................... 85 Using MVC and MFC together ..................................................................................................... 86 Undoing a delete command ........................................................................................................... 86 Chaining controllers together......................................................................................................... 87 Using the MVC architecture ................................................................................................................. 87
www.roguewave.com
WHITE PAPER
Integrating your model class .......................................................................................................... 87 Integrating your viewport class...................................................................................................... 88 Integrating your controller class .................................................................................................... 91 Using MVCs Undo/Redo architecture ................................................................................................. 92 Conclusion .................................................................................................................................................. 96
www.roguewave.com
WHITE PAPER
Executive Summary
This section is for busy readers who need a quick overview of Objective Toolkit. It briefly describes major product features. For a more in-depth technical analysis of the product, begin reading at the Introduction on page 4. To help you determine which programming approach best suits your needs, we also encourage you to read the section entitled Comparing MFC Extensions to Other Component Architectures, beginning on page 7.
www.roguewave.com
WHITE PAPER
MDI (Multiple Document Interface) alternatives and enhancements Objective Toolkit offers the following MDI alternatives and enhancements: Multiple Top-level Interface (MTI), Floating Document Interface (FDI), and Workbook Document Interface (WDI). View components CView extensions that provide features such as advanced zooming and panning. Zooming lets the user zoom in and out of a view and automatically handles all mapping mode changes. Image components A group of components that let users read, write, convert among, and manipulate popular image formats. Supported formats include DIB, GIF, JPEG, PCX, TGA, and TIFF. Utility components Utilities for data encryption and decryption in two different modes, file compression and decompression, accessing file system functions, random number generation, formula evaluation engine, and encapsulation of the Win32 Registry APIs. Data Extraction Classes Used to extract data from text streams. Docking Views is an enhancement to MDI that allows you to convert any document window (for example, MDI child) into a docking window. It allows you to dock complete MFC CViews and frame windows, providing the user with visual focus cues that automatically update the view menus and implement complete dockability. The docking views architecture allows views to float as MDI children, dock to the main frame, and also float outside the main frame. When a view is docked, you can resize the dockable views using the splitter bars positioned along the windows edge. When a view is floating as an MDI child or outside the main frame, you can resize the frames embedding the view in both directions. The Layout Manager framework manages resizing tasks through the use of a framework that implements plug-in layout algorithms and gives you the flexibility to design custom layout managers based on unique business needs. The framework includes several sample layout algorithms such as grid bag layout, relative, scaled and others. It also affords you the flexibility to design custom layout managers based on your needs (for example, for low-resolution displays). The layout manager plugs seamlessly into your existing dialog, formview, controlbar, property page, or any other window to allow for nested layouts. You can integrate the layout manager into applications in a matter of minutes. An Advanced Docking Windows (ADW) architecture, built on top of the Layout Manager framework, provides container independence, complex layout logic, and a drag-and-drop framework. It provides a powerful and flexible mechanism for docking any layout node such as any CWnd or Device Context rendered image (for example, a bitmap) to any other layout node that supports the proper docking interfaces. An ActiveScript hosting engine gives MFC applications complete access to Microsofts ActiveScript technology so they can host scripts. It also provides a mechanism for two-way interaction between MFC applications and ActiveScript so that you can quickly incorporate
www.roguewave.com
WHITE PAPER
VBScript and JavaScript scripting into your own MFC-based applications. A forms-editing engine works in concert with the ActiveScript hosting engine to give you the ability to edit and script MFC graphical user interfaces interactively, using a Visual Basiclike forms-editing interface. The Model-View-Controller (MVC) framework extends DVA so that you can easily separate command handling, view, and document/model information. MVC also makes it easier to create portable views that are application-independent. Build Only Components Required with Build Wizard. Reduce the size of deployed libraires by building only the required subset to reduce the overall footprint. Use the Build Wizard to generate a custom library and DLL. MFC extensions to the basic Microsoft Agent ActiveX components provide Agent-enabled Dynamic Data Validation, Tooltips, Message Boxes, and Tip of the Day support. They also provide a framework with a simple API to initialize the AgentServer, load/unload a default agent character with a default notification sink, and create Acts (critical and non-critical) associated with ActIDs. Namespace Extension Wizard-helps you quickly create a working namespace extension project. After the wizard generates the project, you can easily extend the generated skeleton namespace extension to a fully functional namespace using the classes in OT. Hyperlink Classes provide a convenient way to add point-and-click navigation to conventional non-browser-enabled applications. Web Browser Extensions provide utility functions and a COM Server handy for working with DHTML interfaces in C++. The APP (Asynchronous Pluggable Protocol) ATL Object Wizard provides a very convenient way to insert the OT APP ATL Object into any ATL project. The inserted object provides default features like parsing and validating the supplied URL, spawning a worker thread to perform asynchronous data fetch, and the ability to abort the operation gracefully. The COM object contains an implementation object to which it delegates all of the function calls. Subclassing the implementation object will provide you with virtual notifications and allow you to customize the default behavior. All Rogue Wave Stingray Studio MFC extension components include Visual C++-compatible source code. Objective Toolkit has been tested and optimized to run with Visual C++ 7.1/MFC 7.1, Visual Studio 2005/MFC 8.0, and Visual Studio 2008/MFC 9.0. Objective Toolkit provides developers with components that simulate the Visual Studio .NET/Office XP look and feel or the Microsoft Office 2003 look and feel. Objective Toolkit components provide the XP look and feel on the Windows XP platform, and when used with Visual Studio .NET, some components provide additional Visual Studio .NET look and feel features. Objective Toolkit includes full integration of the Class Reference and User Guide with
www.roguewave.com
WHITE PAPER
the Visual C++ MSDN help viewer, an Objective Toolkit AppWizard with automatic code generation for selected features, and full integration with the IntelliSense auto-completion utility. Its advanced docking windows architecture supports advanced features such as nested docking windows, containerindependent docking, real-time dragging, alternate docking layout logic, real-time splitter movement, fixed-size dockable windows, and more. A BuildWizard is included to minimize build time. Objective Toolkit is fully integrated with all other Stingray Studio components, including Objective Grid, Objective Chart, Objective Views, and Objective Edit.
Introduction
Many of the latest Windows applications incorporate innovative user interface features and graphical components. These featuressuch as the enhanced docking window capabilities in Microsoft Visual Studio, or the drag-and-drop customizable toolbar in Microsoft Wordhave universal appeal and benefit a myriad of Windows applications. Despite the popularity of these powerful enhancements, the Microsoft Foundation Classes (MFC) provide only the most basic user interface features and components. In addition, some of the frameworks provided by MFC are not suitable for all applications. In fact, many of these frameworks have become obsolete since they were first introduced. For example, MFCs Document/View Architecture (DVA) is designed to help separate the user interface from the data in an application. Time has shown that this architecture actually reduces code reuse. Another example is the choice of basic window management offered by MFC. MFC offers only Multiple Document Interface (MDI) and Single Document Interface (SDI) window development, despite the fact that most Windows applications no longer use this paradigm. Objective Toolkit is an MFC extension collection that extends and complements the MFC library. It provides more than sixty drop-in components that cover areas not addressed by the MFC library. Unlike other C++ components, Objective Toolkit components work seamlessly with MFC and, in most cases, inherit from existing MFC classes such as CView or CWnd. Objective Toolkit enables you to bypass the tedious task of writing extensions for common user-interface tasks. This white paper is a technical introduction to the MFC extension architecture and the major features of Objective Toolkit.
www.roguewave.com
WHITE PAPER
Features
The components provided in Objective Toolkit can be divided into the following categories: Control components: Advanced GUI components such as owner-draw buttons, bitmap buttons, menu buttons, well buttons, color well, pop-up color well, masked edit, browse edit, editable list box controls, 2D and 3D tab controls/tabbed windows, calculator control, calendar control, currency control, date/time edit control, marquee control, progress control, customizable toolbar, status bar, and an enhanced functionality tree control. User interface extensions: A variety of user interface extensions, including splash window, tip of the day, bitmapped dialog, workspace manager, keyboard shortcut, run-time editing, user tools menu, and thumbnail components. Extended control bar architecture: A set of MFC extensions that augment its docking window features. The extended control bar architecture and its supporting components give applications the same sizable docking window capabilitywith cool lookthat is available in the Visual C++ IDE (Integrated Development Environment). These docking windows also provide an autohiding feature similar to window pinning feature found in the Visual C++ .NET (2003, 2005, and 2008) IDE. MDI (Multiple Document Interface) alternatives and enhancements Objective Toolkit offers the following MDI alternatives and enhancements: Multiple Top-level Interface (MTI), Floating Document Interface (FDI), and Workbook Document Interface (WDI). View components: CView extensions that provide features such as advanced zooming and panning. Zooming lets the user zoom in and out of a view and automatically handles all mapping mode changes. Panning is a popular scrolling extension used in various Windows applications. Image components: A group of components that lets users read, write, convert among, and manipulate popular image formats. Supported formats include DIB, GIF, JPEG, PCX, TGA and TIFF. Utility components: Utilities for data encryption and decryption in two different modes, file compression and decompression, accessing file system functions, random number generation, and encapsulation of the Win32 Registry APIs. Data Extraction Classes: Data extraction classes are an object-oriented solution for scanning and extracting data from text streams.
10
www.roguewave.com
WHITE PAPER
Compatibility
Objective Toolkit is fully compatible with the latest 32-bit releases of Visual C++. Companies like Bristol and Mainsoft have ported some versions of Objective Toolkit to UNIX.
MFC Extensions
MFC extensions, like MFC, are a group of C++ components that are built on and extend MFC classes. All Stingray Studio MFC extension components include complete Visual C++-compatible C++ source code and can be built as either static libraries or as AFX DLLs, which are DLLs that share a DLL copy of MFC with your application and can be shared between several applications. Since C++ includes support for object-oriented components, MFC extensions are a natural way to extend MFC. Our MFC extensions fit so seamlessly with MFC that you might not notice that you are using a different product.
11
www.roguewave.com
WHITE PAPER
You can also load OCXs at run time, so you dont have to know about them at compile time.
The drawbacks of the OCX (run time) interface include: It is difficult to modify or subclass the defined behavior of the component. You can change various exported properties, but OCXs do not support C++ idioms like inheritance. Implementation inheritance is supported with MFC extensions, but not with ActiveX controls. Since a binary interface needs to be well defined, you must learn a completely new set of terminology and technology. Figure 1 diagrams the OCX interface. Attempting to simultaneously learn about both the OCX interface and a complex environment like MFC is time-consuming and difficult. Performance is an issue. Because OCXs are loaded at run-time, there is often a noticeable delay when the ActiveX COM layer loads the object and prepares it for use. There are ways to shift this delay to application startup, but this does not really solve the problem. The benefits of MFC extensions (compile-time) interface include: MFC extensions are easier to use and modify than OCXs. You can use MFC extensions the same way you use MFC classes. You can modify them with proven C++ techniques without having to learn any complex interfaces. Implementation inheritance is supported with MFC extensions.
DLL Components
Another popular component architecture used primarily by C and C++ developers is a library of C
12
www.roguewave.com
WHITE PAPER
functions that are built into a Dynamic Link Library (DLL). Some of these components even include MFC class libraries that wrap the C function calls to give them a C++ interface. The benefit of DLL components is that they are easy to learn and use. Most of the DLLs written in C originated from DOS and are fairly robust libraries. The drawback of these DLL componentseven ones that provide an MFC wrapperis that they are not object-oriented and thus not easily modified or subclassed. Because the MFC wrapper classes directly call functions in the DLL, you need to determine how to override behavior through the DLL interface instead of the typical MFC methods.
Q: Do I need the functionality and look and feel of the MFC 9 Feature Pack? A: If yes, link in Stingray Studios Feature Pack MFC extension. A migration guide is provided.
13
www.roguewave.com
WHITE PAPER
Button Control
The Objective Toolkit button components provide versatile buttons that have more features and are easier to use than those provided by MFC. Figure 2 illustrates the bitmap, menu, and well button types associated with the Objective Toolkit button components.
Figure 2 Bitmap buttons, menu buttons, well buttons, and embedded color wells
SECOwnerDrawButton SECOwnerDrawButton simplifies the task of creating an owner-draw button. SECBitmapButton SECBitmapButton simplifies creation of buttons that include a bitmap and optional text caption on the face of the button. The top right quadrant of Figure 2 displays examples of bitmapped buttons.
SECMenuButton SECMenuButton provides a simple button that displays a pop-up menu to the right or below the button when a user clicks it.
14
www.roguewave.com
WHITE PAPER
SECWellButton SECWellButton provides a color selection button. The face of the button displays the currently selected color. When the user clicks the button, a color appears below the button that allows the user to select a color.
The Objective Toolkit color well components provide a sophisticated color selection control. This control displays the 20 system colors in a 4 x 5 grid. The user selects a color by clicking the cell containing that color. Each color well can have an additional button that displays a common color selection dialog when the user clicks it. This dialog allows the user to select a color other than those displayed on the face of the grid. SECColorWell SECColorWell implements an intuitive color picker window that you can embed in a dialog window or a CFormView. The color well automatically sizes to fit its content and you can apply a 3D raised border to it. SECColorWell optionally supports palette realization of its color.
15
www.roguewave.com
WHITE PAPER
SECPopupColorWell SECPopupColorWell has been designed for use with the SECWellButton component. It builds on the functionality of the SECColorWell component to provide a pop-up color selection window. This window self-destructs when it loses focus or when a color is selected.
Masked Edit Control
SECMaskEdit provides a subclassed CEdit that lets you specify the format and restrictions on the data entered. For example, if you want a user to enter a phone number, you set the mask to (###) ###-####. Then, SECMaskEdit can handle moving the cursor and restricting user input to digits where the # mask characters are located.
A browse edit is a Windows edit control with a browse button positioned immediately to its right. The user can either type a value in the field or press the browse button and select a value from a dialog. A browse button is a push button with the label ... When you press a browse button, a dialog appears. This modal dialog contains possible values for the user to select. After the user chooses a value from the dialog, the value appears in the text field.
SECBrowseEditBase SECBrowseEditBase is an abstract base class that provides the interface and some of the functionality of a browse edit control. SECBrowseFileEdit SECBrowseFileEdit provides the functionality of a filename edit control. A filename edit is a browse edit that is customized for entering a filename. In a filename edit, the user can either type a filename or pick one from a dialog. SECBrowseDirEdit SECBrowseDirEdit provides the functionality of a directory edit control. A directory edit is a browse edit that allows the user to enter a directory name. The user can either type a directory name in the field or select one from a dialog.
16
www.roguewave.com
WHITE PAPER
Marquee Control
SECMarquee is derived from CWnd. Like any other control, it can be embedded in a view or dialog. The marquee can be used anywhere a CStatic can be used with scrolling enhancements.
Progress Control
The SECProgressCtrl includes several enhancements to the existing progress control, such as smooth fill with percentages, multidirectional progressions (left to right, right to left, bottom to top, or top to bottom), fully configurable foreground/background coloring, and custom status text with configurable fonts. The progress control also offers powerful virtual hooks for you to provide your own customizations easily.
Editable List Box Control
An editable list box is a Windows list box that contains items that the user can modify interactively. The user can modify the order of list box items, create and delete items, and edit the content of items. The editable list box is similar to the Visual Studios Include and Library path editors that are accessible from Tools|Options. You can edit text in place by selecting the item and typing or you can browse for a value by clicking the browse button to the right of the item. A browse button is a push button labeled ... that is only visible after the user double-clicks on an item. When you press the browse button, a dialog directs the user to choose from a list or tree of possible values. After the user picks a value and closes the dialog, the selected item appears in the list box. The following figure illustrates the SECListBoxEditor and the SECListBoxFileEditor components in action. The four buttons above each list box permit the creation, deletion, and reordering of items.
17
www.roguewave.com
WHITE PAPER
SECListBoxEditor SECListBoxEditor implements an editable list box that supports item creation, deletion, reordering, and in-place text editing. However, it does not support browsing. If browsing is required, you must derive a class from SECListBoxEditor and override its OnBrowse() method. SECListBoxFileEditor SECListBoxFileEditor is an editable list box that is customized for entering a list of filenames. An SECListBoxFileEditor object enables users to enter filenames by typing or by picking from a file selection dialog. SECListBoxDirEditor SECListBoxDirEditor is an editable list box that is customized for entering a list of directories. An SECListBoxDirEditor object enables users to enter directory names by typing or by picking from a directory selection dialog.
Tab Control/Tabbed window Components
The tab control/tabbed window components work with the document/view interface to give the user a choice of either two- or three-dimensional tabs for switching between views. The following figures show tabbed windows and tab controls with two and three dimensions.
18
www.roguewave.com
WHITE PAPER
SECTabControlBase SECTabControlBase is an abstract base class that defines the interface API of a tab control. A tab control is a small, rectangular window that draws tabs and processes mouse events. SECTabControl SECTabControl implements a tab control with a two-dimensional look and feel. The SECTabControl inherits its interface from SECTabControlBase and adds the implementation details that define its appearance. SEC3DTabControl SEC3DTabControl implements a tab control with a three-dimensional look and feel like Visual Studios Project Workspace window. Tabs can now be positioned top, bottom, left, or right. The SEC3DTabControl inherits its interface from SECTabControlBase and adds the implementation details that define its appearance.
19
www.roguewave.com
WHITE PAPER
SECTabWndBase SECTabWndBase is an abstract base class that defines the interface of a tabbed window that contains a tab control and one or more child windows (one per tab in the tab control). A tabbed window contains and manages the layout of a tab control and one or more child windows (or pages). When a tab is selected with the mouse, the associated page is activated. SECTabWndBase supports a rich set of operations that allow new views to be added, deleted, renamed, activated, scrolled into view, and so forth. In addition, the appearance of the tabs can be customized to use different colors, fonts, tab shapes, and more. SECTabWnd SECTabWnd implements a tabbed window with a two-dimensional look and feel. The SECTabWnd inherits its interface from SECTabWndBase and adds the implementation details that define its appearance. The SECTabWnd interface is modeled after the popular MFC class, CSplitterWnd. SEC3DTabWnd SEC3DTabWnd implements a tabbed window with a three-dimensional look and feel similar to Visual Studios Project Workspace window. Tabs can be positioned top, bottom, left, or right. The SEC3DTabWnd inherits its interface from SECTabWndBase and adds the implementation details that define its appearance.
Calculator Edit Control
Figure 13 demonstrates usage of the Objective Toolkit calculator and pop-up calculator edit controls.
20
www.roguewave.com
WHITE PAPER
SECCalculator SECCalculator implements a calculator edit control capable of decimal arithmetic and other standard operations. SECPopupCalculator SECPopupCalculator creates a popup window for the calculator that is destroyed whenever the = key is pressed. SECCalcBtn The base class of the buttons on the calculator is SECCalcBtn. A helper class called SECCalcBtn::Attrib defines the attributes used for drawing the calculator button.
Calendar Control
SECCalendar is a CWnd-derived control class that implements a calendar control for date entry and display. Through the use of member functions, days on the calendar can be highlighted and selected. If you use the derived SECPopupCalendar component, you can view the calendar control as a drop-down window to save screen space. Figure 14 demonstrates the use of SECCalendar.
Two components implement the currency edit control: SECCurrencyEdit and SECDropEdit.
21
www.roguewave.com
WHITE PAPER
SECCurrencyEdit is an extensible component for entering and displaying custom formatted currency data. Input data parsing and output display formatting can be customized by deriving new components from SECCurrencyEdit and SECCurrencyEdit::Format. The following figure illustrates use of the Objective Toolkit currency edit control.
SECDropEdit SECDropEdit adds a combo drop-down button to an edit control. This component is used with SECCurrencyEdit and SECCurrencyEdit::Format to implement the currency edit control. SECCurrencyEdit::Format is a nested helper class that provides the core currency formatting and parsing functions.
Customizable Status Bar
Objective Toolkits custom Windows status bar has more features and is easier to configure than MFCs CStatusBar.
22
www.roguewave.com
WHITE PAPER
SECCustomStatusBar allows you to configure the fonts, text alignment, and foreground and background colors in status bar panes. In addition, the custom status bar incorporates a progress indicator that can be programmatically shown in place of the status bar panes. When there is no progress to illustrate, the progress indicator is hidden. You can also embed icons in status bar panes. SECCustomStatusBar also provides support for handling mouse events in status bar panes.
Customizable Toolbar
Objective Toolkits customizable toolbar components extend traditional toolbars by allowing the user to easily drag and drop toolbar buttons into custom toolbar configurations.
The preceding figure is an example of the highly dynamic dialogs you can implement with the customizable toolbar components. In variety and configurability, these toolbars are comparable to those found in Visual Studio. SECCustomToolBar SECCustomToolBar lets your users create customizable toolbars from scratch. Each toolbar can be assigned a set of buttons in a style geared toward a specialized task. From a Customize dialog, a user
23
www.roguewave.com
WHITE PAPER
can add or delete buttons and set a style from default or custom categories. For example, one set of buttons can represent common File operations and another set can represent a user-defined task. In the toolbar dialog, the user can choose between large or small buttons, enabled or disabled ToolTips, and conventional appearance or the cool look. After a user selects a button, it can be dragged to any toolbar. SECCustomToolBar objects are derived from SECControlBar, not from CToolBar/SECToolBar. The following components work in conjunction with SECCustomToolBar: SECToolBarManager manages customizable toolbars and the state of an applications main frame during customize mode, which is invoked by SECToolBarsDlg, SECToolBarsPage and SECToolBarCmdPage. SECToolBarsBase provides a common code base for SECToolBarsDlg and SECToolBarsPage, which provide a user interface for listing and manipulating toolbars. SECToolBarsDlg displays a list of toolbars to the user. This component implements the Customize dialog, which allows a user to select toolbar buttons, manipulate default toolbars, and create a toolbar from scratch. In the toolbar dialog, the user can choose between large or small buttons, disabled or enabled ToolTips, and conventional appearance or the new cool look. SECToolBarCmdPage presents all the available toolbar buttons to the user. These buttons can be dragged onto an existing toolbar or used to create a new toolbar. This component should be used in conjunction with a toolbar manager and SECToolBarSheet. It cannot be used directly in a CPropertySheet. SECToolBarSheet constructs a property sheet used in conjunction with toolbar button templates created by SECToolBarCmdPage and SECToolBarsPage. SECToolBarsPage constructs a toolbar property page for the Customize dialog implemented by SECToolBarsDlg. SECNewToolBar constructs the New Toolbar dialog, which is invoked from the Customize dialog.
Shortcut Bar Control
SECShortcutBar is a new metaphor for a container class. This container class can hold any embedded CWnd-derived objects. Because the shortcut bar is a CWnd-derived class, it can also be embedded anywhere. Accordingly, you can embed shortcut bars in docking windows or the pane of a splitter window, or allow them to overtake the entire client area of the frame window.
24
www.roguewave.com
WHITE PAPER
SECListBar is a specialized SECBar that knows how to insert and remove items from its associated CWnd. The CWnd needs to be an SECShortcutListCtrl or an SECShortcutListCtr-derived class.
SECDateTimeCtrl SECDateTimeCtrl is a date/time edit control. This component works in conjunction with SECDTGadget, which is an abstract base class for the gadget components used by SECDateTimeCtrl.
25
www.roguewave.com
WHITE PAPER
SECDTGadget SECDTGadget is an abstract base class for the gadget components used by SECDateTimeCtrl, which is a date/time editing control. There are several subclasses that are based on SECDTGadget: SECDTStaticGadget implements a non-editable text gadget. SECDTNumericGadget implements an editable numeric gadget. SECDTListGadget implements a text gadget with a defined list of possible strings. SECDTButtonGadget implements a simple push button gadget. SECDTSpinGadget implements a spin button gadget.
Extended Win32 Tree Control
A tree control is a window that displays a hierarchical list of items, such as the headings in a document, the entries in an index, or the files and directories on a disk. The Win32 tree control in the Windows common controls has some limitations, the most significant being its lack of support for multiple selection. Objective Toolkit includes an extended Win32 tree control that adds support for multiple selection, plus an integrated grid with resizable header row, wordwrap, extended ToolTip functionality, alternative viewing modes (for example, current branch + parents), and various color options. Each option is controlled by a style that permits the feature to be enabled or disabled. The following diagram compares MFCs tree control with Objective Toolkits extended version.
26
www.roguewave.com
WHITE PAPER
SECTreeCtrl By deriving from SECListCtrl, the SECTreeCtrl component is capable of multiple selection, allowing a range of tree items to be selected. Each node of the SECTreeCtrl has an associated SECTreeCtrl::Node item. Each tree node consists of a text label and icons indicating selection states. Each node can have a list of items (or children) associated with it. By clicking a node, the user can expand or collapse the associated list of items. In addition, SECTreeCtrl implements a grid on a dialog template. This enables: Wordwrapping of text labels ToolTips on nodes Highlight and caret to occur simultaneously
27
www.roguewave.com
WHITE PAPER
Alternative tree view modes (normal or grey parents) SECTreeCtrl has several auxiliary components, including SECTreeCtrl::Node, SECTreeNode, SECTreeNodeX, and SECTreeCtrlToolTips. SECListCtrl SECListCtrl currently contains a subset of functionality required to implement the SECTreeCtrl derivative. For this reason, SECListCtrl is not intended for general use. By deriving from SECListCtrl, SECTreeCtrl allows a user to select multiple items from a range of items. SECTreeCtrl also allows a user to click an item to expand or collapse its associated subitems. SECListCtrl implements a list view control that holds data associated with SECTreeCtrls nodes. A list view control displays a collection of items; each item consists of an icon and a label. Additional information about each item can be displayed in columns to the right of the icon and label. SECListCtrl manages columns and their headers as well as rows of data. Individual cells in a row can contain text strings or bitmap images, or both. The control can be set with or without grid lines. The default mode is on. SECTreeCtrl::Node This component provides state and draw information for each node. Each node of an SECTreeCtrl-based hierarchical tree control nests an SECTreeCtrl::Node item. Moreover, the value of a particular nodes HTREEITEM equates to the SECTreeCtrl::Node* for that node or SECTreeCtrl::Nodes base classes, which are SECTreeNodeX and SECTreeNode. Each nodes item consists of a label and an optional bitmapped image, and each item can have a list of sub-items associated with it. By clicking an item, the user can expand and collapse the associated list of subitems. A node can appear in two view modes: normal tree view or grey parents mode. SECTreeCtrlToolTips SECTreeCtrlToolTips adds ToolTips to an SECTreeCtrl-based tree control. A ToolTip is a small pop-up window that presents a short description of an item or a prompt to the user. ToolTips are displayed when the user positions the mouse over an item in the tree for a period of time.
The Objective Toolkit workspace manager provides a powerful, yet easy to implement mechanism for saving and restoring multiple window and control bar configurations. Designed with extensibility in mind, the workspace manager provides many useful virtual functions to hook in your own custom storage
28
www.roguewave.com
WHITE PAPER
protocol or media (registry, file, network, and so forth), or to customize existing functions. In addition, the workspace manager exports an effective, easy-to-use dialog for administration of these workspaces. The workspace save/restore feature is an enhancement that does not require you to change or redesign your user interface to use it. Moreover, the workspace save/restore user interface enhancement can be used with the Stingray Studio MDI, FDI, MTI, or Extended Control Bar Architecture enhancements. The workspace manager tracks the creation and destruction of all windows in the application. It can save and then later restore the position, dimensions, and content of all windows. To gather the information required to restore the content of a window, the workspace manager calls upon the document/view architecture. As long as your application is document/view-based, the workspace manager works automatically. The workspace manager consists of two components: SECWorkspaceManager and the new, more powerful SECWorkspaceManagerEx. SECWorkspaceManagerEx features the following enhancements over SECWorkspaceManager: Persistence of workspaces directly to either a file or the registry Persistence of multiple views per document Persistence of an applications current activation state Workspace versioning Full support for Objective Toolkit docking windows, toolbars, and menubars Support for Docking Views SECWorkspaceManagerEx is easier to integrate into your application than the old SECWorkspaceManager. It is easy to extend with your own custom configuration information and your own custom persistence protocol. SECWorkspaceManager is now offered solely for backward compatibility, so if you have never used Objective Toolkit, you will want to use SECWorkspaceManagerEx exclusively. All data persisted by SECWorkspaceManagerEx is stored in a tree of SECPersistentTreeNode (PTN) objects. Each object can have an arbitrary number of key-value pairs to represent one piece of data. This model is similar to the Windows system registry. By default, SECWorkspaceManagerEx stores the application state in a preset tree where settings are grouped logically according to their role (for example, controlbar settings are in one subtree and child
29
www.roguewave.com
WHITE PAPER
frame windows in another). By overriding the appropriate Open() and Save() workspace manager methods, you can easily add your own child PTN objects to be included in the workspace state.
Keyboard Shortcut Components
The Objective Toolkit keyboard shortcut components provide an easy way to allow users to redefine the keyboard for your application. These components are able to update the accelerator table at run time with key bindings that the user chooses. In addition to the implementation details required to edit the key bindings at run time, Objective Toolkit also includes a shortcuts dialog that you can reuse in your application. The following figure demonstrates how to use the shortcut dialog to make a keyboard assignment for the File|Open command.
Figure 22 Shortcut dialog awaiting a new keyboard assignment for the File|Open command
SECShortcutTable SECShortcutTable contains key bindings in the form of an array of ACCELs. SECCommandList SECCommandList contains the list of all command IDs that can be assigned together with a short and a long description. Any and all parts of SECCommandList can be defaulted.
User Tools Menu Components
Objective Toolkits user tools menu components implement a user-configurable tools menu like the one in
30
www.roguewave.com
WHITE PAPER
Microsoft Visual Studio. The user can add custom menu items and specify what action occurs when the menu item is selected.
SECUserTool SECUserTool provides an abstraction of a user tool. A user tool is an executable that can be spawned programmatically by the application. An SECUserTool object encapsulates the filename, commandline arguments, and initial directory that describes how and where to run the executable. In addition, the SECUserTool interface contains an Execute() member function that uses these attributes to launch the user-defined tool. SECUserToolsDlg SECUserToolsDlg implements a user tools dialog. A user tools dialog allows a user to edit a list of user tools, where each tool is represented by one SECUserTool object. In this dialog, the user can create new user tools, edit and delete existing user tools, and reorder the list of user tools.
Thumbnail Components
Many Windows applications like Microsoft PowerPoint support thumbnailing. Thumbnailing lets the user preview files without having to open them. Objective Toolkit provides thumbnail support through the document/view architecture. The following sections describe the Objective Toolkit components that implement thumbnail support.
31
www.roguewave.com
WHITE PAPER
SECTNBitmap SECTNBitmap is a CBitmap derivative that creates, saves and displays thumbnail images. SECTNDC SECTNDC is a CDC derivative that is passed to your SECTNViews OnDraw() method. The view draws the thumbnail image onto the SECTNDC, and Objective Toolkit then handles conversion to an SECTNBitmap and saves the image. SECTNDocument SECTNDocument is an optional CDocument derivative that stores an SECTNView thumbnail image during serialization and bypasses the thumbnail image when reading. SECTNFileDialog SECTNFileDialog uses a CFileDialog derivative to display a thumbnail. It also automatically reads and displays the thumbnail image when the user clicks a file name in the dialog. SECTNView SECTNView is a CView derivative that can automatically generate a thumbnail by drawing onto an SECTNDC. You can override this behavior so that applications can implement their own thumbnail drawing routines using an interface similar to CView printing.
32
www.roguewave.com
WHITE PAPER
SECTNWinApp SECTNWinApp automatically creates an SECTNFileDialog when the user selects Open from the File menu.
33
www.roguewave.com
WHITE PAPER
SECControlBar This component adds the internal state and implementation details required to support sizing when docked. It is a simple step to re-derive existing CControlBar-based classes from SECControlBar. SECDialogBar This component derives from SECControlBar and duplicates the CDialogBar implementation details. Re-derive your CDialogBar-based classes from SECDialogBar. SECToolBar This component adds support for sizing the toolbar when it is docked. SECToolBar derives from SECControlBar and duplicates the CToolBar implementation details. It is easy to re-derive your CToolBar-based classes from SECToolBar. SecToolBar also supports multiple toolbar bitmap resources. SECStatusBar This component serves as the base class for your applications status bar. SECStatusBar does nothing more than re-derive from SECControlBar so that all member variables and implementation details exist as the extended control bar architecture expects. Embed CWnd-derived control inside its pane.
34
www.roguewave.com
WHITE PAPER
SECMenuBar SECMenuBar replaces your menu with a dockable bar that has the look and feel of the Visual Studio and Office 2003 menus. SECMenuBar uses SECCustomToolBar as a base class. Consequently, not only does your menu bar have the cool flat look, but it is also completely customizable when used in conjunction with SECToolBarManager. SECDockState This component makes it possible to save control bar position and size, and then restore it in a later session. To add this feature, change all occurrences of CDockState in your application to SECDockState. SECFrameWnd This component adds the implementation details required to support enhanced docking window capabilities. Re-derive your CFrameWnd-based classes from SECFrameWnd. SECMDIChildWnd This component adds the implementation details necessary to support the enhanced docking window capabilities. SECMDIChildWnd derives from CMDIChildWnd. Re-derive your CMDIChildWnd-based class from SECMDIChildWnd. SECMDIFrameWnd This component derives from CMDIFrameWnd and adds the implementation details necessary to support the enhanced docking window capabilities. Most of the code introduced by the component consists of internal details. To make use of this code, you need to re-derive your CMDIFrameWnd-based class from SECMDIFrameWnd.
MTI can best be described as a combination of SDI and MDI. As in SDI, each top-level window manipulates one document, but as with MDI there can be multiple open documents. MTI creates a new top-level window for each new document. Each new top-level window can include its own menu bar and tool bars. With the Windows taskbar, each MTI window can be easily activated.
35
www.roguewave.com
WHITE PAPER
This approach is common in other GUI operating systems such as the OSF/Motif windowing system for UNIX. Now it is becoming commonplace in Windows as well. Figure 26 illustrates the Multiple Top-level Interface alternative.
SECToplevelFrame SECToplevelFrame is the basis for the MTI MDI-alternative. MTI applications derive from SECToplevelFrame. An MDI application derives from CMDIChildWnd.
Floating Document Interface (FDI)
FDI is like MDI except the MDI children float freely on the desktop instead of being confined to the MDI parent window. As with MDI, you can have multiple documents open. FDI creates a new floating child window for each new document. Each FDI child window appears as a top-level window and manipulates one document. Each FDI child window can optionally include a menu bar containing menu items specific to that window. FDI-based applications have a look and feel similar to the Microsoft Visual Basic environment.
36
www.roguewave.com
WHITE PAPER
SECFDIChildWnd SECFDIChildWnd is a document window similar to an MDI child window, except that it floats freely on the desktop instead of being confined to a parent frame. FDI applications derive from SECFDIChildWnd while MDI applications derive from CMDIChildWnd. SECFDIFrameWnd SECFDIFrameWnd is a main frame window which adds support for the Window menu and the Windows dialog. FDI applications derive from SECFDIFrameWnd, while MDI applications derive from CMDIFrameWnd.
Workbook Document Interface (WDI)
The Objective Toolkit WDI components enhance MDI by allowing the user to place all documents in tabbed windows. This has the benefit of reducing MDI modality. In other words, all of the documents
37
www.roguewave.com
WHITE PAPER
are visible to the user as worksheets, so the user does not have to search the Window menu to locate the document. Figure 29 illustrates the Objective Toolkit WDI interface.
SECWorkbook SECWorkbook is derived from SECMDIFrameWnd and adds the workbook (WDI) interface enhancements. Derive your main frame window class from SECWorkbook if you want to add the workbook interface enhancements. SECWorksheet SECWorksheet is derived from SECMDIChildWnd and adds the WDI enhancements. Derive your MDI child windows from SECWorksheet if you want to add workbook interface enhancements.
Using MTI, FDI, and WDI
These MDI alternatives may seem complex at first, but fortunately Objective Toolkit hides this complexity from the engineer. To change your MFC MDI-based application to an Objective Toolkit MTI- or FDIbased application, derive your frame window classes from one of Objective Toolkits frame window components instead of CFrameWnd or CChildFrame. For example, to convert your application from MDI to FDI, derive your CChildFrame class from SECFDIChildWnd and your CMainFrame class from SECFrameWnd.
38
www.roguewave.com
WHITE PAPER
The gradient caption extension is a frame window enhancement that provides simple emulation of the smooth gradient caption effects found in Microsoft Office. The extension also provides control over the caption text alignment. Note: The Gradient Caption Extension is not available on the Windows XP platform. The gradient caption feature is incorporated into the existing SECFrameWnd and SECMDIFrameWnd.
SECFrameWnd This component derives from CFrameWnd and adds support for the gradient caption. SECFrameWnd also adds support for extended docking window features covered in the section entitled Extended Control Bar Architecture, beginning on page 30. SECMDIFrameWnd This component derives CMDIFrameWnd and adds support for the gradient caption and extended docking window features.
View Components
The Objective Toolkit view components enhance the MFC document/view architecture by providing new features, including printing, print preview, and automatic updating. SECZoomView SECZoomView supports automatic zooming of a view. It supports two modes: zoom-to-fit and zoom normal. In zoom-to-fit mode, the view automatically zooms the view to fit the current size of the window. In zoom normal mode, the developer instructs SECZoomView when to zoom by specifying either a zoom rectangle or a zoom percentage.
39
www.roguewave.com
WHITE PAPER
SECPanView SECPanView builds on SECZoomView and adds panning support. Panning allows the user to grab the view and scroll it in any direction by moving the mouse. SECPanWnd In addition to basic panning support, SECPanView also provides an overview window named SECPanWnd that lets the user see and manipulate the entire view. Figure 31 demonstrates these panning enhancements.
Image Components
The Objective Toolkit image components let the developer manipulate the most popular graphic image types. SECImage SECImage is an abstract base class that defines the interface for each of the format components. This interface includes reading, writing, format conversion, and image manipulation. SECImage supports a variety of image manipulation routines, which include the following:
40
www.roguewave.com
WHITE PAPER
ContrastImage() sharpens or dulls the image. Crop() crops the image to the specified dimensions. FlipHorz() flips the image horizontally. FlipVert() flips the image vertically. Rotate90() rotates the image 90 degrees. The internal SECImage format is documented so you can easily implement your own image manipulation routines using the provided examples for reference. Since SECImage is derived from CObject, it fully supports CObject features such as run time class information, dynamic creation, and serialization. SECImage serialization is useful if you have images stored in your document that you would like to serialize along with other portions of your document. SECDib SECDib supports reading and writing of the popular Windows Device Independent Bitmap (DIB) format. Typically, a file in this format has a .bmp or .dib suffix. SECDib supports all color and compression formats for DIBs. SECGif SECGif provides reading and writing for Graphic Information File (GIF) images. The GIF format is defined by CompuServe and is one of the standard image types used by Internet browsers. Support is also included for interlaced GIF images. SECJpeg SECJpeg supports the JPEG compression scheme found in JFIF (JPEG File Interchange Format) files. This compression scheme is used in many Internet browsers and professional image applications. SECJpeg is based on the version 1.02 JFIF standard. SECPcx SECPcx supports the PC eXchange (PCX) format that many Windows graphics applications use for exchanging images. This format only supports 256 colors. SECTarga SECTarga handles the TGA (TarGA image file) format as defined by Truevision. Video Capturing Applications use TGA. In addition, it was one of the first image formats to support 24-bit color. SECTiff SECTiff can read and write Tagged Image File Format (TIFF) files. TIFF files are popular in scanning
41
www.roguewave.com
WHITE PAPER
applications. SECTiff supports version 6.0 of the TIFF standard and includes support for all image depths and compression schemes.
Image Example
Now that you have seen how the Objective Toolkit image components fit together and have become familiar with the supported formats, lets look at an example. The code below shows you how to load a GIF file, display it, rotate it 90 degrees, and then convert and save the image as a PCX file.
SECGif * pGif = new SECGif; pGif->LoadImage(mygif.gif); //should check for errors. //TODO display image using BitBlt/StretchBlt here. pGif->Rotate90(); //Rotate it! SECPcx * pPcx = new SECPcx; pPcx->ConvertImage(pGif); pPcx->SaveImage(mypcx.pcx);
The components in this section solve non-user-interface problems and perform the following functions: Support data encryption/decryption in two different modes Compress and decompress files Access file system functions Generate random numbers Encapsulate the Win32 Registry APIs SECCryptoFile SECCryptoFile is a CFile derivative that provides encryption and decryption for data written to and read from a file. SECCryptoFile uses a triple-ranked Vigenere cipher that operates in either Electronic Codebook (ECB) or Output Feedback (OFB), which is also known as Cipher Feedback (CFB) mode. You need to supply a password, which can be entered by the user if desired, and choose an encryption method. SECCompressFile SECCompressFile is a CFile derivative that provides compression and decompression for data written to and read from a file. SECCompressFile provides a SetCompressedMode() member function to treat the file as a compressed
42
www.roguewave.com
WHITE PAPER
file type, or a normal CFile type. The user can either treat the entire file as a single compressed block of data or jump to known spots of the file to decompress a smaller portion of the file. SECFileSystem SECFileSystem is a component that allows you to access common file system functions such as retrieving general information about files and directories, reading directories, copying files, and more. You can also access parsing, GUI, and CStringList functions. SECRandom SECRandom provides a method for generating a random number based on a seed value. To skew the results of the random number generation, SECRandom also supports weighted vectors. SECRegistry Objective Toolkit provides an encapsulation of the Win32 Registry APIs. Using our registry abstraction, a developer can modify the registry through a common interface that is consistent under all versions of windows, even back as far as Windows 95. SECRegistry encapsulates the APIs used to access and modify the Windows Registry in a single component. The SECRegistry member functions pertain to the Win32 API.
Miscellaneous User Interface Utility Extensions
The following components implement some other popular features for Windows applications. SECBitmapDialog SECBitmapDialog lets you create dialogs with either tiled or centered bitmaps in the background. This component supports 256 color bitmaps.
43
www.roguewave.com
WHITE PAPER
SECSplashWnd A splash window is a transient window that appears temporarily at application startup. Generally, the popup window displays a copyright notice while the application initializes. SECSplashWnd provides you with a ready-to-use splash window for your applications. All you need to do is insert your own splash bitmap and text. The splash window even deletes itself after a specified time or when the user clicks it, so you do not have to keep track of the splash window in your application. SECTipOfDay If you have used any Microsoft Office application, you are probably already familiar with the tip-of-theday concept. A tip of the day dialog is displayed at start up. It displays random but useful information about the application. SECTipOfDay stores tips in a plain ASCII file so you can easily create tip files (.tip) with any editor. SECTipOfDay is fully customizable. You can specify different fonts and icons, and change other attributes by deriving your own tip class from SECTipOfDay.
SECTrayIcon SECTrayIcon helps MFC developers create applications that are invoked via the Windows tray interface. Animated icons are supported. If you look at the right edge of the Windows taskbar, there is a small tray of application icons and a system clock. This component provides your application with an easy-to-use mechanism to add your own custom icons to the system tray. It also provides UI feedback such as ToolTip text, context menu support, and animated icons.
44
www.roguewave.com
WHITE PAPER
Docking Views Objective Toolkit provides docking windows that enable you to designate any direct CWnd-derived window as dockable. The Docking Views in Objective Toolkit significantly extend dockability by allowing you to dock complete MFC CViews and frame windows. This metaphor provides the user with visual focus cues, updates the view menus automatically, and implements complete view dockability. Figure 35 shows Objective Toolkit Docking Views in action:
45
www.roguewave.com
WHITE PAPER
You can attach a CView to a document, but the document template cannot perform the creation or initialization of the document, view, or parent window. Because the document wasnt created through standard processes, the document manager does not know of its existence, which causes problems during shutdown. A control bar cannot receive focus or become the active window. Control bars are intentionally designed to remain inactive and without focus at all times, whereas CViews are designed to be active and receive focus. This conflict of interest manifests itself in many strange ways, such as focus anomalies. A CView embedded in an SECControlBar cannot receive menu picks because the command routing is not implemented properly. Command handlers in your CView never get called because control bars are not visited when searching for a command handler. You can alter the command routing so that the control bar is visited, but you will never know which control bar to target, because the control bar never becomes active. A CView embedded in an SECControlBar cannot receive keyboard input reliably. Again, this is because the control bar can never become active. Keyboard events are routed to the windowusually the MDI child that is active. The user interface is poorly designed. The problems mentioned above are technical in nature. Even if you overcome the technical obstacles, the resulting UI will not adhere to the standard Windows UI. Lets assume you successfully embed a CView that can receive menu picks and keyboard events inside an SECControlBar. Despite your hard work, this CView still will not adhere to Windows UI standards because the control bars have no way to represent activation or, in other words, there is no caption bar to show activation. This confuses the user because a CView that is receiving menu picks and key presses doesnt have an active caption bar. Worse yet, the window that has the active caption is the most recently active MDI child, which isnt even active. However, because the MDI child has the active caption bar, most users expect menu picks and key presses to occur in that MDI child. Instead, they occur on a control bar that appears inactive. Many engineers assume that Visual Studio is embedding CViews inside its control bars. This is not the case. The Project Workspace window, for instance, is not a CTreeView, its a CTreeCtrl embedded inside the control bar. The Output Window is not a CEditView; its a CEditCtrl. If Visual Studio could dock CViews, then your header and source files would be dockable as well. They are not dockable because Microsoft hasnt solved the problems described above.
46
www.roguewave.com
WHITE PAPER
both. Like a frame window, an instance of SECDockableFrame can parent a CView, CSplitterWnd, SECTabWnd or any CWnd-derivative. An SECDockableFrame also has an optional title bar for showing activation and contains the logic required to change the menu bar when it is activated like a frame window. Lastly, like an SECControlBar, a dockable frame can be docked and resized when docked. The SECMultiDocTemplate class plays a key role in the Objective Toolkit Docking Views architecture. SECMultiDocTemplate is derived from CMultiDocTemplate. The role of CMultiDocTemplate is to create the document, view, and frame window, and then connect them together. It has the following features: Supplies knowledge of the SECDockable frame. Contains the logic to open a new document as a standard MDI child or docking document. Adds a ToggleDocking() member that allows you to open documents between docked and undocked states. Objective Toolkit contains changes and additions to previous Objective Toolkit classes, such as SECMDIFrameWnd and SECMDIChildWnd. These changes ensure that SECDockableFrame objects are included in the command routing class chain so that menu picks are properly routed to a docked document when it is active.
47
www.roguewave.com
WHITE PAPER
The Problem
Whenever you create a dialog, form view, property page, or any other window with child window controls, you need to decide whether to let the user resize the window. You could forbid the resize event, but this leads to an awkward, unfriendly user interface. You could ignore the event, but this results in an ugly, under-utilized window with a disproportionate amount of extra space. Finally, you could trap the size event and code your own custom layout logic. Unfortunately, this requires a large amount of implementation-specific code that is time-consuming to create and difficult to change if you ever want to modify the windows layout. Adding support for resolution-independent positioning requires several more hours of work.
The Solution
Objective Toolkit provides a powerful layout management framework that encapsulates all the details of laying out your child window controls so you can concentrate on content rather than the mechanics of your user interface presentation. With the Layout Manager, you can work with layout constraints, which are easy to understand and change, instead of hard-coded pixel positions that are difficult to understand and even harder to change. As an added benefit, use of a relative constraint-based layout algorithm guarantees that your window or dialog looks the same at any resolution.
Why use the Layout Manager?
The concept of a layout manager is not new. Java, Motif, OWL, and Visual Basic OCXs have been offering layout managers for some time now. However, three features distinguish the Objective Toolkit Layout Manager from the rest: tight integration with MFC, a strong and highly extensible architecture, and ease of integration. 1. MFC integration. The lack of default layout management in MFC has been a noticeable shortcoming for some time now. The Visual C++ IDE offers an excellent mechanism for creating and initializing dialog templates, but it is a static view of your application. Once you run your application and try to resize the window, you must implement your own size logic. The Objective Toolkit Layout Manager is written in MFC for MFC. It plugs seamlessly into your existing MFC application without any modifications to your existing window class hierarchy, so no base class changes are necessary. In addition, the Objective Toolkit Layout Manager offers several calculation and redraw optimizations that minimize the CPU processing and screen flickering that occur with every window resize. This is useful with the increasing popularity of full drag mode for resizing windows. 2. Extensible architecture. The Objective Toolkit Layout Manager is strong and extensible.
48
www.roguewave.com
WHITE PAPER
Most existing layout managers offer just one canned layout algorithm that is tightly tied to a specific dialog or form view implementation. However, the Objective Toolkit Layout Manager is scalable. It has a tree-like division of labor that makes it easy to nest layouts inside of layouts or add your own layout type. In addition, the Layout Manager is not tied directly to CWnd, so you can also position non-CWnd entities, such as a rendered image on a CView. This architecture separates the layout logic from the actual visual implementation to provide a robust and extensible component for reuse in an endless variety of contexts. 3. Ease of integration. Some layout managers force you to abandon your existing window base class and use a canned base class that is layout-aware. Although you may find this acceptable, you should know that it can cause difficulties if you are using a custom base class, like a special CDialog derivative. Because MFC does not adequately support multiple inheritance, it puts you in an awkward position. If you decide to use one of these layout managers, you might need to decide which base class is more valuable to you and discard the other. The Objective Toolkit Layout Manager uses aggregation instead of inheritance to apply its layout logic. Merging it into your application is as easy as instantiating a member layout factory, and producing a listener object that hooks laterally to your existing class with no base class changes. The layout factory tracks memory management for you, so you can merge a layout into your existing window with as little as three lines of source code and one declared member variable.
Layout ManagerArchitecture
The Layout Manager has an architecture that is ideal for reuse and extensibility. Based, in part, on the composite design pattern, the Layout Manager is a collection of nodes arranged in a tree-like hierarchy of responsibility. Layout Nodes The basis for the layout tree is the base class SECLayoutNode, which defines the interface for membership in the tree. A layout node is any object that derives its interface from the SECLayoutNode base class. Conceptually, layout nodes are either proactive or reactive in nature. Proactive nodesalso known as compositesare home to the layout algorithms. Each proactive node encapsulates one layout algorithm. Examples are SECLNRelative, SECLNScale, and SECLNGridBag. Proactive nodes are designed to have and administrate child nodes. Reactive nodesalso known as primitivesare home to the leaf objects intended to be administrated by the proactive nodes. By overriding the appropriate functions in the SECLayoutNode base class, a reactive node can respond to events driven by its parent node, positioning, resizing, and rendering itself as appropriate. SECLayoutNodeWnd is an example of a reactive node. It is designed to link to a CWnd, although you could also design your own reactive node to link to a non-CWnd, such as a rendered graphic in a view.
49
www.roguewave.com
WHITE PAPER
The difference between proactive and reactive nodes is conceptual. Syntactically, both are derived from SECLayoutNode and possess the same type of interface. The intended usage of the object defines its designation. Some objects can be both proactive and reactive. For example, SECLayoutNodeSplitter is both an end-node reactive type and a simple proactive layout algorithm. In general, proactive nodes are not visible entities. For example, an algorithmic layout node is a black box rectangle that is responsible for administrating all of its children within that rectangle. However, one of its children can be another proactive node, which in turn administrates its child nodes as it sees fit, and so forth. This is the strength of a polymorphic layout node in a composite, tree-like hierarchy. Consider the following example. Suppose you have a dialog with three pushbuttons and you want to lay out those pushbuttons in a two-row grid, with one button spanning the entire top row, and the other two spanning the bottom row.
Using nested layouts solves this problem in a snap. At the top of your layout tree, you can use an SECLNGrid grid layout node so you have one black-box grid layout node.
If you configured this grid layout node to have one row and two columns, it would have two layout node children.
The SECLNGrid parent does not care what type of children it creates. Its only concern is that it has two child objects derived from SECLayoutNode. Because you want Child Node 1 to hook to a button, you
50
www.roguewave.com
WHITE PAPER
can use SECLayoutNodeWnd for this task. SECLayoutNodeWnd attaches to a CWnd via aggregation to guarantee type compatibility with the layout-node hierarchy. However, for Child Node 2, you want to administrate both Button 2 and Button 3. The easiest way to do this is to nest another proactive node. Embed another grid node to administrate the two buttons. Now, configure this nested grid node to be a 1 x 2 grid with two SECLayoutNodeWnd children.
Associate one child with Button 2 and the other child with Button 3. Heres the result:
51
www.roguewave.com
WHITE PAPER
As you can see, the only information the top-level grid node possesses is knowledge of its two children: an SECLayoutNodeWnd object and an SECLNGrid object. Because all children must be derived from SECLayoutNode, it can polymorphically manipulate the two children with the same interface. After the top-level grid node positions the nested grid, the nested grid positions its two children and the two SECLayoutNodeWnd objects attached to Button 2 and Button 3. Each parent treats its child nodes as black boxes: there is no parent-grandchild manipulation. This divide-and-conquer division of labor provides a powerful mechanism that is tremendously extensible so you can easily add your own custom algorithms or window types into this framework with little or no change to an existing layout. Window listeners The SECWndListener class is used as a bridge between the Win32 windowing world and the abstracted layout node tree. The window listener attaches laterally through aggregation to an existing dialog, view, control bar, or any other CWnd-derived window and listens for the appropriate windows message to start the layout management recalculations. The listen mechanism utilizes a streamlined window subclass that has a negligible performance hit and does not disrupt the normal message flow in any way. This provides your application with a seamless hook into the layout framework with no need for a base class change. The only purpose of the window listener is to simplify the layout insertion process; you do not need it in order to use the layout framework. An alternate migration path is available on demand. Layout factory The SECLayoutFactory class simplifies the process of creating and merging layout nodes into the Layout Manager framework. Because every componentsuch as a window control, graphical entity, or layout algorithmneeds to be dynamically allocated, you need to allocate a large number of layout node objects. The layout factory simplifies this process by dynamically creating the node type of interest, automatically setting some default parent-child relationships and other various settings, and monitoring all of the allocated storage for self-contained memory management. Typically, you need to instantiate only one layout factory object as a member of your parent window class. Using this one factory object, you can use the layout factory methods to allocate all additional storage required directly in the source. The layout factory can also allocate a window listener object, which is a feature that simplifies layout integration even further. Like the window listener, use of the layout factory is not requiredbut it is highly recommendedfor complete utilization of the Layout Manager. Splitter node The SECLayoutNodeSplitter class easily merges splitter functionality into your layout manager. This powerful splitter class is optimized so you can plug it into the layout framework like any other layout node. This overcomes many of the difficulties that engineers encounter when trying to use CSplitterWnd with any window other than a CView. The SECLayoutNodeSplitter complements any layout node. It
52
www.roguewave.com
WHITE PAPER
can even embed layouts inside a splitter pane, and it works inside a dialog or control bar. Additionally, it supports full drag mode for instant dragging without a tracker, two-dimensional dragging for both threeand four-pane splitters, and useful virtual callbacks for further customization.
Layout managers provided by Objective Toolkit
This section describes each of the default layout algorithms supplied with the Objective Toolkit Layout Manager component. Creating your own custom layouts is addressed in the next section. Alignment Layout. The Alignment Layout aligns a child node relative to the parent alignment node. You can align the child node to the left, right, top, bottom, horizontal center, vertical center, or both. You can also specify top, left, right, and bottom margins. This layout algorithm is useful when used as a nested layout. Scale Layout. The Scale Layout maintains all children with a constant aspect ratio to the parent scale node. In other words, the child nodes top, left, right, and bottom coordinates are stored as percentages of the parent nodes size, and are resolved to actual pixel values with each recalculation. This guarantees a constant aspect ratio no matter what the size of the parent node. Grid Layout. Inspired by the Java Grid Layout, this algorithm allows you to position child nodes in a two-dimensional grid of specific or arbitrary size. The grid can be initialized to a specific two-dimensional matrix, or set to grow arbitrarily in one direction as rows or columns. GridBag Layout. Inspired by the Java GridBag Layout, GridBag is a powerful grid that supports node spanning of multiple rows or columns, or both, variable width columns, variable height rows, variable row/column resize weights to control the rate of change, grid cell insets, grid fill modes, and grid cell anchoring. Relative Layout. The Relative Layout allows a logical organization of layout nodes. Constraints can be set using English-like semantics. For example: set the left side of node 1 equal to the right side of node 2 plus 10 pixels set the bottom of node 1 to 25 percent of the height of node 2 move node 1 such that its bottom is equal to the top of node 2 10 pixels This algorithm is also useful in guaranteeing device-independent positioning.
Implementing custom layout managers
Adding your own layout manager is easy. All you need to do is derive a class from SECLayoutNode and override the OnRecalcLayout() method. A nodes parent calls this method whenever a size/position recalculation is required. In your override, you can reposition your custom nodes children as you see
53
www.roguewave.com
WHITE PAPER
fit by issuing a RecalcLayout() on each child in turn to guarantee the proper message flow. You can examine any of the provided layout algorithms as a model for this customization.
Adding layout management to your applications
The process of merging the layout framework into your application is easy. All you need to do is follow the four basic steps described in the Objective Toolkit Users Guide: 1. Add an instance of the layout factory to the header of the window class to which you want to apply the layout manager. 2. During the windows initialization stage, initialize the layout. 3. Add the relevant layout constraints. 4. Create the window listener and bridge between the parent window and the layout framework.
54
www.roguewave.com
WHITE PAPER
The ADW architecture is essentially a marriage between the highly extensible Layout Manager, which provides container independence and complex layout logic, and the OT drag-and-drop framework. The Layout Manager can be used transparently inside any existing container, such as CWnd, CView, CDialog, and more. It can also be linked to any arbitrary layout element, such as a CWnd control or a non-windowed entity like a bitmap or device context (DC) image. For these reasons, the Layout Manager is an ideal infrastructure for providing a generic docking mechanism.
Why this architecture?
Because the Docking Window extensions are built on top of MFCs existing docking control bar architecture, the number of possible architectural changes to add new features is limited. Instead of implementing elaborate workarounds and extensive code bridges, the cleanest solution is to extend our existing layout management architecture. This strategy circumvents the design constraints imposed by the wide feature set referred to previously in this white paper. The gains in flexibility and extensibility make this migration worthwhile and do not cost Objective Toolkit users any pre-existing functionality. One of the benefits of the ADW architecture is that you can use it seamlessly with existing control bar-based docking windows. A fundamental design requirement of this architecture is complete cooperation with existing control bar-based applications. The ADW architecture shrinks the client area to accommodate its docking infrastructure the same way an OLE object shrinks a frame windows client to accommodate its own tool bars. This allows any existing dockable control bars, including MFCs tool bars and status bar, and the Stingray customizable tool bars/menu bars and Docking Views, to operate with no adverse interaction with the ADW docking infrastructure. In fact, this arrangement leads to a powerful banding-style user interface that you can use to group one set of bars, such as your tool bars, logically outside another, such as ADW dockable nodes in an arrangement similar to that of Microsoft Visual J++ 6.0. In addition, because any CWnd can be wrapped in a dockable ADW node, you can migrate an existing CControlBar to the advanced architecture. The ADW architecture provides some of the advanced docking features that are difficult or impossible to create with the existing MFC dockable control bar architecture. We recognize the significance of the existing Objective Toolkit Docking Window code base and have no intention of abandoning users of this code. Development resources are devoted to improving this component of Objective Toolkit, including customizable tool bars/menu bars and Docking Views, with a strong emphasis on smooth interoperability. Docking inside an MDI child frame One of the strengths of the ADW architecture is that the core docking logic is not tied to a specific parent window. This allows the docking mechanism to be plugged transparently into any container window, including frames, dialogs, and generic CWnds with no modification of the existing architecture.
55
www.roguewave.com
WHITE PAPER
However, Objective Toolkit includes only an external interface for hooking into frame window docking. In subsequent releases, we will add further external interfaces to support seamless dialog and CWnd docking. Fortunately, the frame window interface is still powerful and flexible. For example, you can apply this docking logic to any CFrameWnd-derived object, including CMDIFrameWnd, CMDIChildWnd, SECFrameWnd, SECMDIFrameWnd, SECWorkbook, and more. Note that the ADW architecture does not require the usage of the Stingray SEC docking frame classes (SECFrameWnd, SECMDIFrameWnd) from Objective Toolkit, which is an important distinction if you want a lighter footprint. In addition, note that you can apply the docking logic to an MDI child frame window. Floating MultiDock mode Another addition to the architecture is the floating frame MultiDock capability. This feature allows you to dock more than one dockable node inside the same floating window and then re-dock that entire floating window as one entity. Real-time drag mode Real-time Microsoft Office-style drag-and-drop mode is also available. This feature allows the contents of a docking operation to update instantly in response to mouse movement before the mouse button is released. This is similar to how the Microsoft Office-style tool bars update instantly in response to a drag and drop. Alternate border layout logic By default, the frame docking feature is configured so that the top and bottom borders receive precedence over the left and right borders. For example, the top and bottom borders always occupy the entire available client width, whereas the left and right borders shrink to accommodate the height between the top and bottom borders. This mode is configurable so that the left and right borders can receive priority over the top and bottom.
56
www.roguewave.com
WHITE PAPER
Advanced docking configurations You can use the insertion constraint object SECDockInsertionConstraints in conjunction with SECFram
eDockingFeature::DockNode() to achieve more advanced docking configurations with relative ease.
57
www.roguewave.com
WHITE PAPER
One of the primary advantages of this mechanism is that it allows easy reuse of similar constraints across multiple insertions. In addition, the relative insertion constraints are automatically updated to reflect the last insertion at each docking call. ADW splitter styles The ADW architecture allows you to reconfigure the look and feel of each splitter in your application, at run time, via the SECSplitterBase::SetDrawingStyleAll() static member function. Below are two examples of the available splitter styles.
58
www.roguewave.com
WHITE PAPER
59
www.roguewave.com
WHITE PAPER
JavaScript
Java Script is a compact, cross-platform, object-oriented scripting language, originally developed by Netscape. A JavaScript-enabled browser, such as Internet Explorer or FireFox, interprets JavaScript statements embedded in an HTML page. This enables you to create server-based applications similar to Common Gateway Interface (CGI) programs. The JavaScript language resembles Java and supports most of its expression and basic control-flow constructs. However, there are fundamental differences. JavaScript lacks Java static typing and strong type checking and supports a smaller number of data types.
VBScript
VBScript is a scripting language that is upwardly compatible with Visual Basic for Applications (VBA), which currently ships with Microsoft Office and Visual Basic. Internet Explorer 4.0 ships with a COM server that implements a VBScript engine. Unlike VBA, it can be leveraged into your existing applications free of licensing or royalty charges. Aside from language syntax and semantics, one of the key differences between VBScript and JScript is that VBScript has a complete server-side role, whereas JScript does not. For example, VBScript can be integrated with the ISAPI component of Microsofts Internet Information Server (IIS) to run a variety of back-end applications.
Hosting an ActiveScript
Before making your application scriptable, you need to determine the applications object model. The object model is the specification of the objects in your application that are available to the scripting engine through OLE automation. Internet Explorer, for example, exposes objects that are appropriate for HTML authoring, such as the document, form, window, and browser. The objects you choose to expose for your application should be specific to your problem domain. After you have determined the object model for your application, you are in the implementation phase. To host a script, you need to implement the necessary ActiveX scripting COM interfaces, such as IActiveScriptSite and IActiveScriptSiteWindow. By implementing these interfaces, you expose the properties and methods for each object in your object model and enable manipulation of these objects from VBScript or JavaScript, or both. Objective Toolkits SECAScriptHost class provides a default encapsulation and implementation of these two interfaces so you only need to instantiate an SECAScriptHost to accomplish the aforementioned steps.
ActiveScript classes
There are many classes in the ActiveScript framework that work together to simplify the addition of scripting capabilities to your applications. These classes are described in detail below.
60
www.roguewave.com
WHITE PAPER
SECAAppObj SECAAppObj defines an application object that can be loaded from an ActiveScript script. An application object and a form object (SECAFormObj) work together to create new forms or load existing forms. An application object has the member functions that implement form creation and loading. The form object can return the application object that created it. Via the application object, you can create another form if required. The SECAAppObj supports OLE automation and exposes the commands OpenForm, NewForm, and
FormByName. You can call these commands from your scripts by calling the OpenForm(), NewForm(),
and FormByName() member functions of the SECAAppObj class. SECAAppObj derives from CCmdTarget and implements a dispatch map. SECAFormObj SECAFormObj is an ActiveX control that implements a scriptable form object to link your script to your application. SECAFormObj supports OLE automation and exposes commands for manipulating forms such as Show, Hide, Minimize, GetApp and more. The GetApp command returns an application object (SECAAppObj) that you can use to create additional forms. In addition, the form defines events that it sends to its container such as OnLoad() and OnUnload(). SECAScriptHost The script host implemented by SECAScriptHost is a COM object that implements the IActiveScriptSite, and IActiveScriptSiteWindow interfaces. Microsoft defines the IActiveScriptSite, and IActiveScriptSiteWindow interfaces as part of their ActiveScript technology. A script host can create and destroy either the VBScript or JScript script engine, create a new top-level OLE automation object as part of the script, set a reference to the window hosting the script, and parse and execute a script. An application that requires scripting must instantiate a script host and use this pointer to create the scripting engine and execute a script. SECAScriptOccManager SECAScriptOccManager implements an OLE control container for use with ActiveScript. It creates a script control site and a script control container. You must instantiate this class in your applications
InitInstance() member to enable support for containment of ActiveScript controls and hosting
COM objects. IActiveScriptErrorHandler IActiveScriptErrorHandler is the basic interface for receiving and handling Active Scripting runtime error notifications. Your class that hosts scripts needs to derive from IActiveScriptErrorHandler and override its
HandleError member.
61
www.roguewave.com
WHITE PAPER
The Objective Toolkit Users Guide has detailed instructions on using and leveraging the ActiveScript Framework. The ScriptMDI sample includes a dialog that demonstrates the canonical form of an ActiveScript host. Refer to the CDlgScript class in the sample for an example of the minimalist ActiveScript host. An MDI sample, scriptMDI, is also provided if you want to add scripting to an MDI application.
The classes that implement the ActiveForm framework are: SECScriptHostDoc, SECScriptHostView, SECAFloatDocTemplate, and SECADlgFrame. SECScriptHostDoc SECScriptHostDoc is an ActiveForm document that derives from COleDocument and IActiveScriptErrorHandler (through multiple inheritance) and manages the process and state of scripting and editing the form. For example, the SECScriptHostDoc implements the ability to toggle between edit and run mode, create and delete script items, handle ActiveScript error notifications and creation, and destroy and serialize forms and their contents. You can pass the SECScriptHostDoc directly into your doctemplate instantiation or you can derive your own document class from it and then customize its behavior.
SECScriptHostView The SECScriptHostView, a CFormView-derived class, is a specialized form view that implements drag and drop of OLE controls onto its surface. It also implements a user interface for sizing, positioning, and setting the properties of contained ActiveX controls and manages the tool bars that accompany it. You can pass the SECScriptHostView directly into your doc-template instantiation or you can derive your own view class from it and customize its behavior. SECAFloatDocTemplate SECAFloatDocTemplate facilitates the creation of new forms for scripts that want to provide support for dynamic form creation. Use this class in conjunction with SECScriptHostDoc, SCScriptHostView, and SECADlgFrame to provide suitable support for scriptable frame creation. When the script executes a NewForm
62
www.roguewave.com
WHITE PAPER
or OpenForm function, SECAAppObj responds by instructing the SECAFloatDocTemplate to create a new form. The forms created by the SECAFloatDocTemplate object typically are SECADlgFrame-derived classes. SECADlgFrame SECADlgFrame implements a simple SDI top-level frame utilized by ActiveScript scripts during dynamic form creation. The SECAFloatDocTemplate object instantiates a frame of this type whenever the script creates a new form via the commands OpenForm, NewForm, and FormByName. The Objective Toolkit Users Guide describes how to incorporate the ActiveForm functionality into your applications. The ScriptMDI and ScriptSDI samples illustrate how to use the ActiveForm framework in MDI- and SDI-based applications. The following figure shows the key features of the Objective Toolkit ActiveScript/ActiveForm support:
63
www.roguewave.com
WHITE PAPER
Introduction to MVC
MVC is an object-oriented framework that provides a clear and concise definition for how to construct reusable and extensible graphical user interface parts. Despite its lack of widespread use, the model-viewcontroller architecture is not a new concept. It was inventedalong with the graphical user interface and the concept of object-oriented programmingabout thirty years ago by researchers at the Xerox Palo Alto Research Center (PARC). The culmination of that research was the Smalltalk language and its multi-windowed, highly interactive Smalltalk-80 interface. Even by todays standards, the invention of Smalltalk and MVC are revolutionary. To some degree, nearly every user interface developed in the last three decades has been an adaptation of the work done at Xerox PARC. In fact, MVC has been partially reproduced in many other development environments. DVA itself is an adaptation of the concepts introduced by the MVC paradigm. However, it can be argued that the key purpose of MVCreusewas lost in the adaptation. Although DVA is based on the sound design principle that data and presentation should be kept separate, its implementation of this ideal compromises reuse, modularity and scalability. This section shows how you can resolve many of the problems caused by the drawbacks and limitations of DVA by applying some of the concepts of MVC to MFC. This adaptation requires you to use DVA in a new wayone that adheres to the principles of MVC.
64
www.roguewave.com
WHITE PAPER
However, do not assume that DVA is the right architecture for you, especially if you are developing large, complex applications. Despite a proven track record, DVA lags behind in some areas, which we will describe in the next section. The net effect is a reduced capacity for reuse and an inherent lack of scalability. Specifically, although DVA can support an application containing two or three CDocument- or CView-derived classes of lesser complexity, it strains under the weight of numerous documents and views. When DVA was first introduced, C was the programming language of choice for Windows application engineers. In fact, many of us had never even used C++. Consequently, one of Microsofts primary goals when developing the MFC implementation of DVA was ease of comprehension, even at the expense of object-orientation and scalability. At that time, introducing a more complex (albeit more object-oriented and reuse-friendly) architecture would have made the paradigm shift in Windows development from C to C++ a bit too difficult for most engineers. Accordingly, DVA is a relatively thin layer of C++ code on top of the procedural-based Windows event model. Although DVA is ideal for developing applications like the Visual C++ Scribble sample, it is inadequate for developing large, highly object-oriented applications. This is why many experienced engineers find DVAs scope to be limited. Because the development framework you use affects the organization and structure of your application, your framework dictates ease of application development and maintenance. No segment of code should be trusted with such influence without careful scrutiny. The larger your project, the greater the likelihood that DVA is not the solution for you.
It could be argued that DVA is not responsible for the problems with the Scribble sample. It could be argued that the problems are due to a violation of the principle of encapsulation. However, there are problems inherent in DVA that tend to encourage this violation. First of all, it is not clearly defined where in DVA the application data and visual representation should be separated. As a result, it is unclear
65
www.roguewave.com
WHITE PAPER
how DVA maps onto an applications problem domain. For some applications, such as a simple word processor, the answer seems obvious: an MFC document represents a user document. However, this is not true for applications like interface builders or graphical tools. The data for such applications might include colors, fonts, pens, and window bounds, all of which need to be saved. However, it seems inappropriate to include this information in a CDocument-derived class because it describes a visual representation of the underlying data. It is unclear how you would separate the document and view classes in this scenario. Because DVA is vague and contains few usage restrictions, its easy to make a bad decision, as was done in the Scribble sample.
DVAs approach is monolithic
DVA takes a monolithic approach to application decomposition. Because documents and views cannot easily be nested, a DVA application typically contains large, highly functional document classes paired with equally large, highly functional view classes. Of course, it is possible to place one CView-derived class inside another, but that construct is not specifically supported by DVA. Instead, you must perform all message forwarding yourself. Moreover, although its certainly not a requirement that CDocumentderived classes be large and bulky, they often become that way because engineers add representation information to the document class so that it can be serialized along with the data. Consequently, DVA is not suitable for decomposing an application into small building-block classes that can be composed into larger ones. With DVA, the result is large, cumbersome classes that complicate the model and make it difficult to divide an application along functional boundaries. Due to the lack of modularity and numerous interdependencies inherent in DVA, development teams usually find it difficult to subdivide a problem and work in parallel. In fact, the lack of modularity usually requires every team member to know the entire application model. These types of dependencies translate into reduced team productivity and reduced application stability.
CViews inhibit reuse
CView is one of the most significant and fundamental building blocks of any MFC-based application. In fact, MFC programmers often place a majority of their code in CView-derived classes, particularly in graphics-intensive applications. The role of CView leads many engineers working with MFC to assume that Microsoft has constructed this class with due diligence, taking care to ensure that the class that houses 30-70% of your code is reusable. This is not the case; CViews are not reusable. Unfortunately, many engineers arrive at this realization late in the development cycle and pay dearly. A CView isnt reusable for a number of reasons. Most importantly, a CView depends on an applicationspecific document type. This dependency renders it unusable in applications that have a different document type. To illustrate the point, assume you are developing a set of CView-derived classes that are to be reused throughout your company. Further, assume youve decided that reusable views must adhere
66
www.roguewave.com
WHITE PAPER
to DVA. This means that for each view you declare, you must also create an associated document type that the view understands and expects whenever and wherever it is used. The implication is that wherever you use the reusable view, you must use the document. This causes problems when you want to use the view in an existing application that has an incompatible document class. There are a number of imperfect solutions available. First, you can change the base class of the reusing applications document from CDocument to the document type the reusable view expects. This approach eventually forces you to use multiple inheritance from your document class, making your code difficult to read and maintain. MFC discourages using multiple inheritance from two CCmdTarget-derived classes due to the complications that can arise. For that reason, this solution is fragile and ill advised. Another solution is for every engineer across a company to use the same common document class. You then can exchange all CViews among projects. Unfortunately, it is difficult to choose which document class to use. Even if you do establish a common document class, maintenance of the selected document often becomes difficult or impossible as the documents become more complex. In addition, a document with an interface wide enough to serve all possible view types is usually an undesirable solution anyway. You may also be tempted to make all data accesses go through virtual functions rather than directly accessing the members of a document. When you access data through virtual functions, you remove the reusable views dependency on any document. However, you cannot access data through virtual functions and adhere to DVA, so its not a viable solution. In addition, accessing data through virtual functions, rather than directly from the document, is not prescribed in the Scribble sample set. Typically, an engineer retrofits the virtual data access methods after realizing that CViews are not reusable. The best solution, short of creating an alternative to DVA, is to limit the functionality in CView. Ideally, CView should only contain an OnUpdate() handler. All of the drawing logic should be contained within a completely reusable CWnd. In this context, the purpose of the CView is to adapt the reusable CWnd for use within a particular application that defines a document and updates hints. This solution concedes that CViews are not reusable, so no potentially reusable code is placed in a CView. In this solution, the CView acts as an adapter, or a wrapper, that translates the weakly-typed messaging protocol, defined by the documents update hint, into calls to a strongly-typed C++ interface, defined by the CWnd child of the CView. One problem with this approach is that you miss some of the chief CView features, such as print and print preview support.
CView/CDialog are incompatible
One of the limitations of MFC is the incompatibilities between CView and CDialog. For example, MFC bans the placement of a CView-derived class inside a dialog. If you want to create a reusable GUI
67
www.roguewave.com
WHITE PAPER
component that can be used inside a frame window or a dialog, you must confine your design to CWndderivation, which requires you to avoid DVA altogether. However, you can use templates as a workaround for this problem.
OnUpdate creates problems
Ironically, one of the most popular functions of DVA, the UpdateAllViews() and OnUpdate() notification mechanism, is also one of the most problematic. It is important to design your notification system carefully. Otherwise, your OnUpdate() handlers can easily grow into pages of switch statements and weakly-typed hints that are difficult to read and maintain. In addition, because this notification mechanism is a weakly-typed interface, coding errors are not caught at compile time. When engineers use Scribble as a model, they tend to use notifications at too low a level. In other words, update hints tend to be numerous and detail-oriented, which leads to pages of weakly-typed checks in the OnUpdate() handler. DVA does not describe the best use of the notification mechanism. It only passes you an opaque pointer to a hint. This lack of specificity often leads engineers to embed the API to their component inside the notification hints to the OnUpdate() handler. For example, suppose you were developing a Windows Explorer-like application for the display of hierarchy data. In this type of tool, youd include a splitter window that contains a tree view on the left and a list view on the right. The typical approach to this type of user interface is to maintain the hierarchy data in a document that is observed by both tree and list views. The synchronization of the two panes is accomplished by passing update hints via
CView::OnUpdate(). When DVA and its OnUpdate() mechanism is used at too fine a granularity, the
end result is an interface to a logical control or widget that is embedded in the hints. For example, you should avoid code that looks like this:
UpdateAllViews(this, HINT_NODE_EXPANDED, pNode);
Generally speaking, a CView has two responsibilities. First, it must present the data contained in the document to the user. Second, it must interpret and react to user input. Its role imposes several limitations. Both presentation and control are packaged in one class, which means that if you reuse a views presentation data, you also need to reuse the views reaction to user input.
68
www.roguewave.com
WHITE PAPER
the traditional input, processing, and output roles into the GUI realm: Input Controller --> Processing --> Model --> Output --> View
The user input, the modeling of the external world, and the visual feedback to the user are separated and handled by model, viewport, and controller objects. The controller interprets mouse and keyboard inputs from the user and maps these user actions into commands that are sent to the model or viewport, or both, to effect the appropriate change. The model manages one or more data elements, responds to queries about its state, and responds to instructions to change state. The viewport manages a rectangular area of the display and is responsible for presenting data to the user through a combination of graphics and text. As previously mentioned, DVA is derived from MVC. Consequently, many of the concepts set forth in MVC are similar to concepts in DVA. MVC adheres to many of the same design principles. For example, you separate data from presentation in MVC. However, MVC gives a concise definition of how to accomplish this separation. In addition, MVC provides a more thorough and full-featured architecture than DVA. Because it is well defined, it is obvious how to add common pieces to the architecture, such as undo/redo support, scripting mechanisms, record and playback, and more.
MvcModel
MvcModel has some of the same responsibilities as CDocument. For example, both classes manage information and notify observers when that information changes. However, there is an important distinction. In MFC, a CDocument often becomes a storehouse for unrelated information. The edict is: if it must be saved, put it inside a CDocument. When this edict is followed, the documents design becomes a function of what needs to be serialized rather than what needs to be modeled. An MVC model is a more concise abstraction than a DVA document because it contains only data and functionality that are related by a common purpose. If you need to model two groups of unrelated data and functionality, you need to create two separate models. Another key difference between a model and a CDocument is that a model encapsulates more than just data and functions that operate on it. A model is meant to serve as a computational approximation or abstraction of some real-world process or system. It not only captures the state of a process or system, but how the system works. This makes it easy to use real-world modeling techniques to define your models. For example, you could define a model that bridges your computational back-end with your GUI frontend. In this scenario, the model wraps and abstracts the functionality of a computation engine or hardware system while acting as a liaison that requests the real services of the system it models.
69
www.roguewave.com
WHITE PAPER
MvcViewport
MvcViewport is similar to the MFC CView class. We chose to call the class a viewport instead of an MvcView to avoid an overlap in terminology and to make the distinctions between a view and a viewport obvious. The MvcViewport class is responsible for mapping graphics onto a device. A viewport typically has a one-to-one correspondence with a display surface and knows how to render to it. A viewport attaches to a model and renders its contents to the display surface. In addition, when the model changes, the viewport automatically redraws the affected part of the image to reflect those changes. As with DVA, there can be multiple viewports onto the same model and each of these viewports can render the contents of the model to a different display surface. The biggest difference between CView and MvcViewport is that a viewport is not derived from CWnd. The designers of MFC chose to implement the concept of a view through derivation, whereas MVC implements the same concept through aggregation. This is a common theme throughout MFC and a common distinction relative to MVC. In many cases, where MFC and DVA use derivation, MVC uses aggregation. In this case, a window aggregates one or more viewport objects and then delegates to them when a paint event occurs. Because a viewport avoids the CWnd derivation and does not create a physical window, a viewport is significantly lighter than a CView. A viewport simply takes a pointer to a device context and draws to whatever device context it is passed. In addition, because a viewport can be aggregated into any window, such as CFrameWnd, CWnd, or CView, you can display a viewport inside a CWnd, a CView, or a dialog box without modification. In addition, support for printing and print preview is preserved. Another difference between MvcViewport and CView is that MvcViewport should not contain menu handlers or command handlers. Instead, delegate this responsibility to the views controller object. Additionally, an MvcViewport class can easily be nested but a CView cannot. For example, an MvcViewport can be a composite viewport that contains several subviews that can also contain a dditional subviews.
MvcController
A controller is the means by which the user interacts with the application. A controller accepts input from the user and instructs the model and viewport to perform actions based on that input. In effect, the controller is responsible for mapping user action to application response. For example, if the user clicks the mouse button or chooses a menu item, the controller determines how the application should respond. The MvcController class has no equivalent in MFC. In MFC, the CView is both the view and controller. It is responsible for presentation and control. In small applications, it can be simpler to handle user input in the view instead of the controller. However, in
70
www.roguewave.com
WHITE PAPER
large applications, putting user input in the controller affords you greater modularity, reuse and control exchangeability. Unlike DVA, MVC gives you a choice. You can preserve the separation or collapse presentation and control into one class. If you want a separate controller, derive your viewport from MvcViewport and your controller from MvcController. If you want presentation and control in one class, derive your viewport from both MvcViewport and MvcController.
Connecting the model, viewport, and controller
The model, viewport, and controller are entwined and in constant contact. Consequently, they must reference each other. The figure below illustrates the basic Model-View-Controller relationship:
The figure above shows the basic lines of communication among the model, viewport, and controller. In this figure, the model points to the viewport, which allows it to send the viewport weakly-typed notifications of change. Remember that the models viewport pointer is only a base class pointer; the model should know nothing about the kind of viewports that observe it. By contrast, the viewport knows exactly what kind of model it observes. The viewport also has a strongly-typed pointer to the model so that it can call any of the models functions. In addition, the viewport also has a pointer to the controller, but it should not call functions in the controller aside from those defined in the base class. You should keep dependencies between the viewport and controller minimal if you want to swap out one controller for another. The controller has pointers to both the model and the viewport and knows the type of both. Because the controller defines the behavior of the triad, it must know the type of both the model and the viewport to translate user input into application response.
Additional reading on MVC
Before we introduce more information on MVC, wed like to recommend some additional references on the subject. MVC is regarded as a classic example of a design pattern. It has recently experienced
71
www.roguewave.com
WHITE PAPER
a resurgence in popularity. The classic text, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma et al (ISBN 0-201-63361-2), describes MVC and the Command design pattern. However, its coverage of MVC is minimal. The more recent text, A System of Patterns: Pattern-Oriented Software Architecture by Frank Buschmann et al (ISBN 0 471 95869 7), provides an in-depth description of MVC and the Command Processor design pattern within the context of C++.
The following sections delve more deeply into the MVC framework and the individual classes that comprise it.
MvcVisualComponent
A visual component is an object that can draw itself to any given device context. In essence, a visual component is a rectangle with a draw function. The MvcVisualComponent base class defines an interface from which all other displayable objects in MVC ultimately derive, which allows you to treat a collection of visual components consistently. The MvcVisualComponent class is intentionally light to accommodate applications that may need to instantiate more than one object. A visual component maintains its size and position relative to its containers origin. Because its origin is an offset from its containers origin, if the container moves, all sub-components move with it. A visual component doesnt address units of measure of the size and position values. That is the containers
72
www.roguewave.com
WHITE PAPER
responsibility. If the container measures its client area in inches, the bounding rectangle of the visual component is in inches. Another MvcVisualComponent or derived class contains an MvcVisualComponent. Any visual component derivative can be nested to any depth. However, ultimately, there must be a viewport type of container at the root of this hierarchy of visual components to map the visual components onto a real device. In other words, visual components cannot render themselves by themselves. They must be told which device context to render to. Only a viewport can know this.
MvcVisualPart
A visual part is a type of visual component that keeps a back pointer to its container. The only other thing it adds is a few member functions to support invalidation. We didnt collapse this class into MvcVisualComponent because we wanted to keep MvcVisualComponent as small as possible so you could instantiate thousands of them in an application. It this case, it isnt necessary or desirable for each visual component to know its container.
MvcLogicalPart
An MvcLogicalPart is a visual part that supports a logical coordinate system. If you have used MFCs CScrollView and the SetWindowExt()/SetViewportExt() members of the CDC, you are already familiar with a logical coordinate system. MvcLogicalPart is similar. The logical part establishes a unit of measure that it uses to measure and draw to its client area. That unit of measure can be pixels, inches, millimeters, or something application-specific. When the logical part is instructed to draw itself, it first prepares the given DC so that its units of measure are in effect for its subsequent drawing. MvcLogicalPart is the largest body of code in MVC. Accordingly, it provides you with substantial functionality. The MvcLogicalPart adds zooming and panning, mapping mode support, a virtual size, and more. There is another important characteristic of MvcLogicalPart: it can be nested. Like any visual component, a logical part can be a child of another logical part. This means a child can establish an entirely different mapping mode than its parent. For example, the parent could measure its client area in inches, although its child measures its client area in twips.
MvcViewport
An MvcViewport is a type of logical part that observes a model and delegates user input to an MvcController for handling. A viewport is responsible for displaying graphics on the screen and reflecting updates in model state. To perform its responsibilities, it generally requests information required
73
www.roguewave.com
WHITE PAPER
for rendering from the model and responds to notifications of change from the model. A viewport is also responsible for routing messages to a suitable handler. By default, it routes all messages to its controller for handling; however, it can be overridden. Consider a viewport that contains several child viewports. In this scenario, the parent viewport might route the messages to one or more children first. The parent viewport would send the message to its own controller only after giving its children a chance to obtain the message. Another important characteristic of the viewport is that it can retain a pointer to the CWnd that contains it. Knowing the containing CWnd is critical to the viewport; it gives the viewport a special significance. For example, a viewport is the only visual component derivative than can retrieve a real window pointer, instantiate a real CDC pointer for rendering, map logically to device coordinates, or perform a real invalidation of a windows client area. We delayed the introduction of a real window that binds to the viewport because you might want to put your visual component hierarchy in a model and render it through multiple viewports. If you were to organize your visual component hierarchy in this way, a visual component, visual part, or logical part might appear in multiple windows, which means that it cannot be bound to a particular window.
MVCWrapper_T
The MVCWrapper_T class implements the Decorator design pattern as described in the book, Design Patterns. A wrapper is a container with a single component. The wrapper provides certain services to its component, such as decorating the component with a border or adding scroll bars to the sides of the component. Wrappers are classes that can be plugged into any window in place of the component it wraps without affecting the window. The ability to add wrappers dynamically without affecting client code is accomplished by endowing the wrapper with the same interface as its component. The MVCWrapper_T class is a template, so it allows you to specify which component to inherit and emulate. Some wrappers are visual and others are not. For example, a wrapper can be used solely to offset the origin of its component. One of the most interesting properties of wrappers is that they can be nested to any depth. For example, you can create a border wrapper that wraps a scroll bar wrapper that wraps another border wrapper that finally wraps the actual component. The Stingray MVC implementation currently has three wrapper classes: MvcBorderWrapper_T, MvcScrollWrapper_T, and MvcBufferedWrapper_T. MvcBorderWrapper_T wraps a border and MvcScrollWrapper_T wraps a scroll bar wrapper. MvcBufferedWrapper_T provides off-screen rendering for the component it wraps. For small memory overhead, off-screen rendering reduces or eliminates flicker during redraw. This is convenient because it provides buffered rendering to a component without requiring any change to the component whatsoever, making buffered rendering an afterthought.
74
www.roguewave.com
WHITE PAPER
IComposite_T
The composite interface includes members for adding, deleting, retrieving, and locating children. Mix this interface into anything you need to make a composite. For example, if you need to create a composite visual component, you can do so by mixing this interface into your MvcVisualComponent-derived class.
IMvcMsgHandler
IMvcMsgHandler is a small interface that can be mixed into any class that requires the capacity to route and handle messages. By default, it is implemented in MvcViewport and MvcController.
The IMvcSubject, IMvcObserver and IMvcMessage Interfaces
The most fundamental of all MVC classes are the subject and observer interfaces. These two interfaces constitute the dependency mechanism that is central to MVC. The 1995 book, Design Patterns: Elements of Reusable Object-Oriented Software discusses what the author calls the Subject/Observer design pattern. The idiom the author describes is similar to the roles of document and view in MFC, where the document is a subject and the view is an observer. However, the subject/observer design pattern is more general. In the subject/observer relationship, a subject encapsulates related data and functionality that an observer monitors. If the state of the subject changes, the observer must know about it. To accomplish this, the subject defines a notification dictionary that is the set of all notifications of change a subject can broadcast. A notification is any class that mixes in and implements the IMvcMessage interface. It is the responsibility of the subject to define and export a notification dictionary and to broadcast individual notifications of change to its list of observers. It is the responsibility of the observer to attach itself to a subject and understand and react to the notifications it receives. As you see, the interfaces act like document and view in some regards. However, some differences do exist. First, the subject and observer interfaces can be mixed into any class via multiple inheritance. Conceptually, IMvcSubject and IMvcObserver are interfaces, not objects. This means that any existing class, including CWnd-derivatives, can become a subject or an observer, or both, by simply supporting the appropriate interface(s). To reiterate, an object can mix in both subject and observer interfaces to yield an object that observes one object and serves as subject to another. This is analogous to a CDocument that is dependent upon other, nested documents. This concept has important implications. It means, for example, that you could have nested models or even a directed cyclic graph of models that are interdependent. This yields the important
75
www.roguewave.com
WHITE PAPER
composition quality that DVA lacks. With respect to subjects or models, you can compose larger, more intelligent models out of smaller, reusable ones. This yields a composite model that can be made into an even larger model in future applications.
MvcModel
Although rich in concept, the MvcModel class is sparse on code. The MvcModel class mixes in the IMvcSubject interface and implements it. MvcModels main purpose is to serve as a base class for your domain-specific models.
The MvcPresentationModel_T class
Generally speaking, there are two types of models: System models Presentation models A system model is an MvcModel-derived class that models some non-graphical, real-world system or process. A presentation model models a graphical presentation. System and presentation models can be used exclusively or in combination. Used in combination, a presentation model provides the presentation for a system model by mapping the system into the graphical realm. This scenario is illustrated below:
The presentation model is a piece that is absent from both DVA and MVC. The presentation model describes where to store GUI data.
76
www.roguewave.com
WHITE PAPER
A presentation model is a templatized class, which inherits from both MvcModel and MvcVisualPart. The use of multiple inheritance here allows the presentation model to function as both a model and a visual-in effect, a visual model. Visual models are particularly useful in diagramming applications. It is intuitive to implement a diagram class as a presentation model. A diagram is a type of presentation model, which manages the graphical symbol data, font choices, pen widths, and more. Like a model, it manages data, albeit graphical data, and exports functionality. However, like a visual part, a diagram can draw itself and even be nested as a symbol inside a parent diagram. A diagram acts as both a model and a visual part. Just because a presentation model can draw itself, do not think that the viewport is devoid of functionality. Actually, the viewport is offloaded to some degree, but it still has a well-defined role. When you use a presentation model, a viewport becomes a gateway through which a presentation model can render itself. In other words, the viewport becomes a perspective on the presentation that defines what subregion of the overall presentation or diagram to display. The presentation model, in effect, is a graphics server that presents whatever rectangular portion of the graphics the viewport instructs it to paint. This rectangle is measured in logical units and corresponds to the zoom factor and scroll positions of the viewport. For a more detailed explanation of the why system and presentation models should be separated, see the section titled Solving Real Problems with MVC, beginning on page 41.
IMvcVirtualPart
This interface is complex. Earlier, in the section entitled OnUpdate creates problems on page 41, we described how difficult it is to read and maintain the large switch statements that usually reside in CView::OnUpdate(). IMvcVirtualPart solves this problem. This interface, used in combination with the presentation model, nearly eliminates the need for the switch statement because OnUpdate() typically is trying to determine which rectangle to invalidate. With IMvcVirtualPart, invalidation no longer goes through the OnUpdate() channel, which eliminates 90 to 100 percent of the cases in the
OnUpdate() switch statement.
IMvcVirtualParts functionality is a variation of the subject/observer design pattern, called the virtual part/renderer pattern. In this instance, you have one virtual part, which is rendered by one or more renderers. Like subject/observer, if the virtual part changes, the renderers need to be informed. However, instead of passing a message from a subject to observer, the virtual part passes an invalid rectangle to all its renderers. The virtual part notifies each render which rectangle in logical coordinates has been invalidated. The virtual part sends this rectangle by calling InvalidateRect(), which then broadcasts the invalidate call to all renders in the following manner:
void MyVirtualPart::InvalidateRect(const CRect& rcLog, BOOL bErase, BOOL bRepairNow)
77
www.roguewave.com
WHITE PAPER
Iterator_T<MvcVisualPart*> i(&m_setRenderers); MvcVisualPart* pRenderer; for (pRenderer = i.GetFirst(); pRenderer != NULL; pRenderer = i.GetNext()) { pRenderer->InvalidateRect(rcLog, bErase, bRepairNow); }
It is the responsibility of the renderer to determine where that logical rectangle is on its display surface and, if it is currently showing the invalid region, to perform a true windows invalidation. The presentation model implements the IMvcVirtualPart interface and the viewports serve as renderers. So, if a presentation model creates a new visual component and positions it within its virtual display surface, it invalidates the visual components bounds. All attached viewports hear the invalidation and trigger a repaint. This technique is convenient. It allows the presentation model to draw and erase within its own virtual space, using its own logical units, without needing to know how the graphics it produces get mapped onto a device.
MvcTransactionModel
A transaction model is a central component of the Undo/Redo architecture. Its primary purpose is to maintain the undo and redo stack. See the section entitled MVCs Undo/Redo architecture beginning on page 41 for more details.
MvcController
The controller makes a viewport seem to respond directly to mouse movements and keyboard activity. A controller receives input from the user and translates that into application response. The application response is in the form of function calls to the model and viewport. The MvcController class is an MVC base class that defines the basic structure and behavior of a controller. This includes the ability to acquire, maintain, and relinquish control. It is also a mechanism for mapping messages to handlers. You can reuse MFCs message map solution, but it is problematic for a number of reasons. Even though a separate controller class for message handlers sounds like a good idea, MFC is designed in such a way that this seems impossible to implement. MFC allows commands such as menu picks to be rerouted to non-windows. However, you cannot reroute windows messages such as mouse clicks. In MFC, only a CWnd can receive windows messages and only one instance of a CWnd can receive the messages for a real Windows window. Because a controller is a non-window that can handle windows messages and exist in multiple instances per window, this seems to be an unworkable solution. An
78
www.roguewave.com
WHITE PAPER
interesting aside: this problem was cited by an original MFC team member as the main reason Microsoft emulated MVC for the CDocument and CView, but stopped short of implementing a controller (MFC Professional Developers Conference, 1995). As previously mentioned, this problem appears to be unsolvable. We arrived at a number of relatively undesirable workarounds, but we never found a solution. Eventually, we discovered a unique trick that allows us to do something MFC was never meant to do: route windows messages, such as mouse clicks, to any number of potential handler classes. We call this solution the window plug-in (SECWndPlugIn). A window plug-in is a class that you can plug into a window to listen to, and potentially handle all messages that the window receives. In essence, its equivalent to a modular MFC message map. Imagine being able to take a large message map, break it up into blocks, and put each block and its associated handlers in its own classes. Then, you could dynamically and conditionally aggregate only the message map blocks you need. In addition, if you had two window types that needed to handle the same messages in the same way, such as mouse wheel support, you could put that code in one place and aggregate this block of handlers in all window types that require the feature. This is exactly what window plug-ins do. The controller class, MvcController, overcomes the aforementioned problems through its derivation from SECWndPlugIn. An MvcController serves as a listener that allows all derivatives to plug into a window and control it. A window can host any number of controllers and the controllers can be delegated to in any order you see fit. Once again, where MFC uses derivation, MVC uses aggregation. The result is greater flexibility and reusability. Another convenient characteristic of controllers is that they are seen by MFC and Visual Studio as a even though they arent. In fact, they dont incur the overhead of a Windows window handle. This allows you to create a standard MFC message map in your derived controller classes and even edit them using Class Wizard. In addition, controllers dont disturb the normal MFC message and command routing channelsthey simply extend them. Furthermore, because controllers route messages using MFCs own highly-optimized message map mechanism, there is virtually no performance degradation.
The collection classes
The collection classes, such as CCArray_T, MvcSet_T, CCStack_T, and more, are simple wrappers around MFCs collection classes. The only reason weve wrapped them is to make it convenient for you to use STL or your own internal collection classes without modifying the MVC framework. Our goal is to remain collection-class-independent without inventing a new set of collection classes and without becoming notationally inconvenient. We accomplished this goal by doing the following:
79
www.roguewave.com
WHITE PAPER
Weve wrapped the MFC collection classes using inheritance. In other words, CCArray_T derives from CArray, so you can still use the collection as if it were a CArray if you like. Weve created a very convenient iterator class called Iterator_T. Using this class, you can iterate over any collection that happens to implement the ICollection_T interface, regardless of whether the actual collection implementation is MFC, STL or otherwise. The following code shows how Iterator_T is used:
Iterator_T<MvcVisualPart*> i(&m_setParts); MvcVisualPart* pParts; for (pPart = i.GetFirst(); pPart != NULL; pPart = i.GetNext()) { // Do something }
The command class is simple. It has two primary virtual functions: Execute() and Unexecute(). As you might expect, these members must be overridden in concrete command classes that you define. Effectively, this moves the implementation of undoable operations out of member functions and into persistent records. This is necessary because there is no way to undo a function call. No record of the parameter values, or even the call itself, is maintained. This is what MvcCommand isa persistent function call, which stores all parameter values so that it can be undone later.
The transaction model
The transaction model is where the command records are stored upon their execution. The transaction model is responsible for storing the commands and undoing or redoing the most recent ones. In terms of implementation, the transaction model maintains two separate stacks: an undo stack and a redo stack. As commands are executed, they are automatically pushed onto the undo stack. If the transaction model is instructed to undo the most recent command, it pops the command off the top of the undo stack and instructs it to unexecute itself. Then, it pushes the command onto the redo stack. If a redo is requested, the opposite occurs.
80
www.roguewave.com
WHITE PAPER
The Undo/Redo interface can be mixed into any class for which you want to provide undo/redo support. By default, it is already supported by the MvcPresentationModel_T. The Undo/Redo interface is straightforward. It includes the Undo() and Redo() member functions in addition to a few other utility and query members.
Tying Undo/Redo into the MVC framework
So far, nothing we have said about the Undo/Redo architecture is MVC-specific. The Undo/Redo architecture ties into MVC by using an MvcCommand to mix in the IMvcMessage interface. This approach allows a command to act as both a persistent function call record and a notification of change, so a model can define a notification dictionary and a command dictionary as one and the same. After a model executes a command that it defines, it forwards the command itself as notification to its observers.
Perhaps the most frequent and most baffling design issue is where to draw the line between model and viewport data and functionality. Consider the following example. Lets assume you are developing a schematic capture application that displays a computers circuitry. The domain data in an application like this would be the components used on the motherboard, the interconnections between pins, the timing characteristics, and more. This information and the functions for accessing it seem to be an obvious candidate for a model class. Next, we need to think about how to present this information to the user. Most likely, we want to show this information in several different ways. For example, we might want to view the information as a schematic or topology-type diagram that shows the layout of the components and their interconnections. We might also choose to view the information as a hierarchical, tree-structure display of the components. Lastly, we might want to supply a table of parts required to assemble the motherboard. So, because we want to present the same data in three ways, this application is an ideal candidate for the viewport class. We could define three separate viewport classes that present the model data in three separate ways. At this point in the design phase, the architecture becomes more ambiguous. Lets say we want to have two different viewports on the same schematic (topology diagram), but at different zoom levels. In this scenario, the circuit data is identical and even the diagramming data is identical. The only difference is
81
www.roguewave.com
WHITE PAPER
that one viewport is zoomed in and one is zoomed out. The problem we need to address is how to present the data. We need to decide: If two different viewports can present the same diagram If we should move the diagram data into the model How to address serialization If we should save the diagram data to a file All indicators seem to support putting presentation data in the model, which seems counter to good design. It overloads the model with two different classes of data and leaves the viewport with little else to do. The prior example typifies the issues engineers consider when deciding how to map MVC or DVA onto the application. In summary, the dilemma is what qualifies data as model data versus viewport data. In some applications, it is obvious how you should define model data. In a text editor, the model data is the text. However, in other instances it is not so obvious. Consider, for example, a GUI builder/forms engine. The model data in this case is the buttons, fonts, sizes, properties, and more. Another good example is Objective Views. Objective Views is a Visio-like MFC- and MVC-based framework for building diagramming applications or complex, structured graphics systems. DiagramModel, DiagramViewport, and DiagramController are at the top-level of Objective Views class hierarchy. Because you want more than one view of the diagram, you might put the squares, ellipses, pen widths, and more into DiagramModel, which seems counter to the design principle of MVC. Lets start by distinguishing among the different types of data and functionality. Typically, GUI applications have four categories of data: Problem domain data Presentation domain data Perspective domain data Selection state The problem domain data is non-GUI. The objects and data in the problem domain exist independently of the rest of the application, even if you are developing a console application. The problem domain usually consists of an MvcModel-derived class that emulates or models some real world process or system in the vocabulary of the problem domain. In our previous example, a schematic of the computers motherboard, the components, interconnections and timing characters is the problem domain.
82
www.roguewave.com
WHITE PAPER
The presentation domain data contains graphical data necessary for rendering. The data in this domain is metadata that references and renders the data in the problem domain into the graphical realm. In the previous example, the visual components, symbols, and links in a schematic diagram that represent the motherboard are part of the presentation domain. The perspective domain is simple. Usually, it is the scroll and zoom positions that specify how the data of the previous two domains should be presented to the user. In conclusion, the selection state is the selected visual components from the presentation domain. Each of these data domains can: Have a one-to-many relationship with the layer directly above them (domain) Vary independently Be serialized independently The first bullet applies to the problem domain. You can have multiple renderings into the graphical domain. For example, the circuit board can be shown as a schematic diagram or a hierarchy by using multiple graphical domains to render the problem domain data in two ways. In addition, each graphical domain can have multiple perspectives. For example, the schematic diagram can be shown twice at different zoom factors. The graphical and perspective domains can also have one-to-many relationships. Each domain can vary independently. Perspective data illustrates this. Lets assume you want to display two different data sets in two different views and lockstep the zoom factor of these views. For example, you could lockstep the zoom factors of a gate-level schematic and a block functional diagram so you could see the same hardware component at different levels of abstraction. The zoom factor could be an IMvcSubject that both views observe. Then, when zooming into the functional diagram, the user would zoom into the gate-level diagram as well. In this example, the perspective domain changes independently of the other domains. However, this isnt a requirement. Changes in one layer can still propagate to other layers via the subject/observer pattern. For example, if you add a new node to the problem domain, the graphical domain must create a new visual component that references and renders it. This implies the need for a subject/observer relationship between the problem and the graphical domains. Alternatively, a base class/derived class relationship could tell the graphical domain that a change has occurred in the problem domain. Lastly, you can serialize each layer independently, which means that you can serialize each layer to a different section of the same file. In addition, you can also save each data domain to a separate file with references among the files. For example, you can save a netlist in one file, a schematic rendering of the
83
www.roguewave.com
WHITE PAPER
netlist in another file that references the file that contains the netlist, and a snapshot file that saves
the zoom and scroll positions that the user defined. Each netlist file can have multiple schematics and snapshot files, or both, referencing it. Now that weve specified how we are going to categorize the data, we need to decide where each category should reside. In DVA and MVC, engineers typically use only two classes, model and view, for storing four domains of data. The result is that these classes are overloaded and overlap (one object mixes two domains together). How you choose to distribute the data domains across the MVC triad depends on your application requirements. Sometimes it is unnecessary to keep the four domains separated. Generally, if you dont need to have the one-to-many relationship between two layers, you can combine the layers. The graphical and perspective domains usually are combined. Sometimes engineers are unsure where to put the graphical domain. As a result, oftentimes the graphical data is put in the model because only the code in a CDocument can be serialized. The decision of where to put data is determined by whether it should be persistent. With MVC, putting graphical information in the viewport doesnt preclude the information from persisting because the viewport receives the serialize command first instead of the model. This is a side effect of the MVC widget definition. In MVC, the widget is the viewport. The model and controller are treated as member variables and implementation details. For example, you can instantiate an MVC-based widget inside a parent without informing the parent of all three objects in the triad. The viewport is a facade for the rest. When the parent wants to serialize the state of its widget, it tells the component to serialize itself. The component is responsible for deciding what data needs to be written to the file, including its model. Consequently, the Serialize() member is a member of the viewport. Now, you no longer need to decide where data should reside based on whether the data should be persistent. Now that we have a choice, we need to decide where to put this data. If you dont need to separate presentation from system information, you can put the data in the viewport. However, if you want to show multiple perspectives on the same presentation, use MvcPresentationModel_T. The classes responsible for each of the four domains of data are: MvcModel-based system model: problem domain data MvcPresentationModel_T: presentation domain data MvcViewport: perspective domain data MvcController: selection state
84
www.roguewave.com
WHITE PAPER
The presentation model creates and maintains MvcVisualComponents that reference a node or object in the system model for which they provide presentation. The presentation model also receives notifications of change from the system model, so it must have an OnUpdate() member. The presentation model acts upon the notifications it receives from the problem domain by creating, destroying or invalidating visual components. Accordingly, the viewport receives a change notification or invalidation request from the presentation model. Lets assume the system model models a nuclear reactor. If a dangerous condition arises, the monitoring system provides input to the system model. The system model then sets a flag internally and forwards a notification of the change to all its observers. Once the presentation model receives the notification, it creates an MvcVisualComponent object to display a red, flashing message. The presentation model subsequently broadcasts an invalidation request to all renders that cause the viewports to paint the alert.
Leveraging the power and flexibility of the subject/observer interfaces
Using nested models, you can create a Model Adapter. A model adapter is an intermediate or translating model that maps the interface and notification dictionary of one model onto the interfaces and notification dictionary of another. Lets assume your model and viewport speak different languages. Lets also assume that your viewport was originally written to observe a different type of model. Accordingly, it only understands the interface and notifications of the model for which it was designed. However, you want to reuse the viewport with a new model. This model has a different interface and notification dictionary, however, many of the same operations are available under different names. A model adapter can act as an intermediary or translator in this scenario. The adapter model is the model to which the viewport attaches. The adapter model observes the foreign model. All calls to operations and notifications go to the adapter model. The adapter model then translates and forwards these calls to the foreign model in its terms. Another difference between the DVA relationship and subject/observer relationship is that in the subject/ observer relationship you can have a many-to-many cardinality between subjects and observers. In MFC, a view expects only one document, however, an observer can depend upon as many subjects as you require. When an observer is notified of a state change it also receives an indication of which subject changed. If you prefer to have one subject per observer, but require the state of multiple subjects, you can create either a composite or umbrella model. An umbrella model is a model that allows multiple models to be presented as one. It observes the component models it contains and passes on the change notifications it receives to its observer. In general, when the subject is a model in an MVC triad, the umbrella model is the preferred approach because the controller doesnt need to depend upon the same list of models that its viewport does. However, if youre only working with subject and observer derivatives, either method will suffice.
85
www.roguewave.com
WHITE PAPER
None of the MVC classes are derived from MFCs CObject class or CWnd class, or both. This means you can safely use multiple inheritance and C++ templates with all MVC classes. This allows you to use MFC window and view classes in MVC-based code by packaging them as MVC classes. For example, if you want to nest a CListView inside your MVC-based viewport, you can inherit a ListViewport from both CListView and MvcViewport. In addition, you can achieve persistence in MVC by inheriting from CObject and any MVC class. For example, if you want to save a collection of MvcVisualComponents during your documents serialization process, all you need to do is inherit your visual components from both CObject and MvcVisualComponent.
Undoing a delete command
One of the most difficult actions to plan in an Undo/Redo architecture is how to undo a delete operation. For example, if we delete the object, a subsequent undo must reconstruct the object, which usually requires a full snapshot of the objects state. There are two ways you can avoid taking a full snapshot using the MVC-based Undo/Redo architecture. The first is to make a copy of the objects state. However, the copy is usually almost as large as the object itself. Additionally, it can be time-consuming, complex, and memory-inefficient to reinstantiate a hierarchy of objects when undoing a delete. However, this is a suitable solution if these issues arent significant in the context of your application. The second solution is to never really delete objects. Instead of deleting an object, remove all the models references to the object, but keep the object. Then, you can move the pseudo-deleted objects to a to be deleted list. If the user then undoes the delete, the application takes the object off the list and repairs the models references. Reference counting allows you to delete the object when appropriate. It also ensures that a pseudo-deleted object can be referenced multiple times by the command sequence undo, redo, undo, redo, undo, redo of a delete operation, even though you now have several commands referencing the object but the model doesnt. To implement reference counting, weve borrowed the AddRef() and Release() members from COMs IUnknown interface. Each time a new command or model references an object, it does an AddRef(). Each time a command expires from the undo buffer and is deleted, the commands destructor does a
Release() as does the model when it removes the object. When the number of references on an object
becomes zero, the object auto-deletes itself. The implications of this solution are multiple. First, you should never call Delete() on objects on which you want to support undoable deletion. Instead, you should call Release() and then nullify the
86
www.roguewave.com
WHITE PAPER
reference. Second, you must inherit all objects for which you want to support undoable deletion from the IRefCount base class to get the AddRef() and Release() members. See the following section entitled Using the MVC architecture for more details.
Chaining controllers together
MVC allows you to chain controllers together. You can have any number of controllers that have been daisy-chained together associated with one view. In this scenario, you have one primary controller, and n secondary controllers. This allows you to achieve an even greater modularity with respect to control. For example, if you wanted to provide a separate OLE drag-and-drop controller, you would write it once and chain it to the end of your existing set of controllers by overriding the primary controllers Create() member and creating the secondary controllers. Then, override OnWndMsg() and OnCmdMsg() and extend the message routing so that secondary controllers are visited as well.
1. Create your MvcModel-derived class. You have a choice of deriving your model from MvcModel or MvcPresentationModel_T. See the section entitled Solving real problems with MVC beginning on page 41 for a description of both choices. For the purposes of this tutorial, well use the presentation model as a base. If you require serialization support in your model, inherit your model from CObject and MvcPresentationModel_T.
class CloudDiagram : public CObject, public MvcPresentationModel_T<MvcVisualComponent> { public: ~CloudDiagram();
To avoid confusion, remember that the CloudDiagram class is a model, even though Model is not part of its name. We could have called it CloudModel or CloudPresentationModel
87
www.roguewave.com
WHITE PAPER
or CloudDiagramModel. However, because a diagram is a kind of presentation model, CloudDiagram is the most concise name. 2. Add your model class as a member variable inside your document.
Class CmyDoc : public CDocument { // Attributes protected: CloudDiagram m_CloudDiagram;
3. Create an accessor member inside your document that returns the model.
CloudDiagram* GetCloudDiagram() { return &m_CloudDiagram; };
4. Override CDocument::IsModified() so that it tests the modified flag of the contained model as well.
BOOL CMyDoc::IsModified() { return CDocument::IsModified() || GetCloudDiagram()->IsModified(); }
5. Override your documents serialize member so that the contained model is serialized as well.
void CMyDoc::Serialize(CArchive& ar) { GetCloudDiagram()->Serialize(ar); }
7. If your model creates and destroys objects for which you want to support undoable deletion, you must derive those objects from IRefCount. See the section entitled Undoing a delete command on page 41 for a complete explanation.
class CloudComponent : public CObject, public MvcVisualComponent, public IRefCount { 8. Override the cloud diagrams Draw() member and implement the presentation of its data. void CloudDiagram::Draw(CDC* pDC) { Iterator_T<CloudComponentPtr> i(GetClouds()); for (CloudComponent* pCloud = i.GetFirst(); pCloud; pCloud = i.GetNext()) { pCloud->Draw(pDC); } } Integrating your viewport class
9. Create your MvcViewport-derived class. At a minimum, your viewport class should specify
88
www.roguewave.com
WHITE PAPER
a GetModel() function that returns a typecast version of your model and override the Draw(), CreateController(), OnInitalUpdate(), SetVirtualSize(), and GetVirtualSize() members. Well describe how to implement these members later in this procedure.
class CloudViewport : public MvcViewport { public: CloudDiagram* GetCloudDiagram() { return (CloudDiagram*)m_pModel; }; virtual void Draw(CDC* pDC); virtual BOOL CreateController(); virtual void OnInitialUpdate(); void SetVirtualSize(int cx, int cy); CSize GetVirtualSize() const;
11. Create your viewport and attach it to the model contained in the document. Call the viewports Create() member and then call its SetModel() member. This initialization is typically done from the OnCreate() member.
int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; CMvcCloudDoc* pDoc = GetDocument(); CloudDiagram* pModel = pDoc->GetCloudDiagram(); m_component.Create(this, NULL); m_component.SetModel(pModel); } return 0;
12.Delegate all calls to OnInitialUpdate() and OnDraw() to your viewport from the Cviewor CWnd-derived class that contains it. This gives your viewport the opportunity to initialize and render itself on the drawing surface of its container:
void CMyView::OnInitialUpdate()
89
www.roguewave.com
WHITE PAPER
13.Next, you need to size and position your viewport to occupy the entire client area of its container.
void MyView::OnSize(UINT nType, int cx, int cy) { m_component.SetOrigin(0, 0); // Position the viewport m_component.SetSize(cx, cy); // Size the viewport CView::OnSize(nType, cx, cy); }
14.Now, you need to define the override members for initializing the viewport to create its controller and define what it renders.
BOOL CloudViewport::CreateController() { // EMPTY FOR NOW. WELL COME BACK TO THIS FUNCTION // IN THE NEXT SECTION. } void CloudViewport::Draw(CDC* pDC) { OnPrepareDC(pDC); GetCloudDiagram()->Draw(pDC); } void CloudViewport::OnInitialUpdate() { SetAxisExtents(X, 4000, 1000); SetAxisExtents(Y, 4000, 1000); }
Note: The OnInitialUpdate() member function is ideal for initializing the logical and container extents of the viewports client area. This statement asserts that for every 1000 units along the X axis in the containers client area, there are 4000 logical units in this viewport. We dont use values like one and four because small values like these allow for zooming in and out. The extents can never be less than one because CDC::SetWindowExt() expects an integer. 15.Next, we need to define the GetVirtualSize() and SetVirtualSize() functions for the virtual size of the viewport.
void CloudViewport::SetVirtualSize(int cx, int cy) {
90
www.roguewave.com
WHITE PAPER
GetDiagram()->SetSize(cx, cy);
The virtual size of the viewport is equated to the size of the diagram because the diagram is rendered through the viewport and can be larger than the viewport. Accordingly, the size of the diagram is the virtual size of the viewport.
Integrating your controller class
16.Create a controller class that knows how to mediate among the user, your model, and your viewport classes. At a minimum, you need to create GetDiagram() and GetViewport() member functions that return a typecasted pointer to your model and viewport classes.
class CloudController : public MvcController { // Constructors public: CloudController(); virtual ~CloudController(); // Overrides public: CloudDiagram* GetDiagram () { return (CloudDiagram*)m_pModel; }; MyViewport* GetViewport() { return (MyViewport*)m_pVisualPart; };
17.Add a message map to your controller class so you can use Class Wizard to manage your message handlers.
// Generated message map functions protected: //{{AFX_MSG(CloudController) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP()
18.Generate a new Class Wizard file that incorporates your new controller class. You can do this by deleting the *.clw files and then running Class Wizard. It prompts you to rebuild the .clw file. 19.Go back to your viewport class (CloudViewport). Override its CreateController() member function to create a new instance of your controller:
BOOL CloudViewport::CreateController() {
91
www.roguewave.com
WHITE PAPER
Remember to set the flag m_bAutoDelCtlr to true so that the instance of MyController is deleted when the viewport destructs. If you dont want the viewport to own the controller, leave the flag set to FALSE, but be sure to delete the controller yourself. It may seem incorrect to have the viewport own the controller and know the type of its controller. However, allowing the viewport to own the controller simplifies the reuse of the viewport without loss of generality, because the controller instantiation and type knowledge can be deferred to a subclass. In other words, define your drawing code in your viewport class and then specify a default controller in the CreateController() member to allow the user to override the CreateController() with their own controller class. 20.To incorporate your controller into your application, include it in the standard message routing so your controller can listen and handle the messages being sent to the containing window. To enable this functionality, you must override two functions: OnWndMsg() and OnCmdMsg().
BOOL CMvcCloudView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // First pump through normal channels. This allows you to // override the components default handling inside the view class. if (m_component.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; else return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } BOOL CMvcCloudView::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult ) { // First pump through normal channels. This allows you to override // the components default handling inside the view class. if (m_component1.OnWndMsg(message, wParam, lParam, pResult)) return TRUE; else return CView::OnWndMsg(message, wParam, lParam, pResult); }
At this point, you have a completely reusable component integrated into your DVA application that defines its control, its data and its rendering.
92
www.roguewave.com
WHITE PAPER
22.Create commands that correspond to every action your controller class can perform on its associated model and viewport. The number, granularity, and scope of these commands are your choice. Below is an example of a derived command class:
class AddCloudCommand : public CloudCommand { MVC_DECLARE_COMMAND() public: AddCloudCommand(CloudDiagram* pDiagram, const CPoint& pt, CloudComponent* pCloud = NULL); virtual ~AddCloudCommand(); void Sprint(CString& strCmd); virtual BOOL Execute(); virtual BOOL Unexecute(); CPoint m_pt; SmartPtr<CloudComponent> m_pCloud;
};
Notice the MVC_DECLARE_COMMAND macro in the class declaration. This macro adds a static member variable to store the type ID of the command. You can avoid the use of macros and declare the static yourself if you prefer. The macro is provided as a convenience. Notice that SmartPtr is used to wrap the reference to the cloud added by the execution of this command. This allows us to reference count the cloud instance so we know when we can free it. 23.Now, we need to define a type ID for the command. Observers that receive the command via their OnUpdate functions require a type ID. Without the type ID, the OnUpdate function cannot determine what type of command it has received. To initialize the type ID for the command, add the following line to the implementation file that contains the command code.
MVC_IMPLEMENT_COMMAND(AddCloudCommand, CMD_ADDCLOUD)
The MVC_IMPLEMENT_COMMAND macro takes two arguments. The first is the commands class name and the second is the type ID to assign it. The type ID is a unique integer, which is #defined to CMD_ADDCLOUD in the above example.
93
www.roguewave.com
WHITE PAPER
24.Next, we need to implement the command. Lets start with the constructor:
AddCloudCommand::AddCloudCommand(CloudDiagram* pDiagram, const CPoint& pt, CloudComponent* pCloud) : CloudCommand(pDiagram) { m_pt = pt; m_pCloud = pCloud; }
When a new cloud is created, we need to know where to position the cloud. This is enough information to instantiate a new cloud. You need to pass in a pointer to a cloud object because the AddCloud command may be undoing a previous RemoveCloud() command. In this case, we dont need to create a new cloud, we just need to insert the original one. The Execute() function makes the insertion later. The AddRef() call is there to ensure the cloud isnt deleted while we are still referencing it. 25.Now, lets add the primary function: the Execute() override. This function completes the task the command is designed to perform:
BOOL AddCloudCommand::Execute() { if (m_pCloud == (CloudComponent*)NULL) m_pCloud = new CloudComponent(m_pt, m_pDiagram->m_clouds.GetSize()); m_pDiagram->m_clouds.Add(m_pCloud); m_pDiagram->InvalidateVisual(m_pCloud); } return TRUE;
26.Next, we need to override the Unexecute() function. This function reverses the effect of the command, which is assumed to have previously executed.
BOOL AddCloudCommand::Unexecute() { MvcCommand* pInvCmd = new RemoveCloudCommand(m_pDiagram, m_pt, m_pCloud); pInvCmd->Execute(); delete pInvCmd; } return TRUE;
Notice that the Unexecute() command determines the inverse of this command and executes it. This is a convenient way to avoid duplicating the code required to remove a cloud. 27.Lastly, you can override the Sprint() member so you can dump textual descriptions of the Command objects that exist in the undo history.
void AddCloudCommand::Sprint(CString& strCmd)
94
www.roguewave.com
WHITE PAPER
{ }
28.Now that weve implemented the AddCloud() command, we need to create, execute, and log it. For this we turn our attention to the model object. The model class defines member functions, which are implemented by creating, executing and logging the corresponding command object. This allows the controller to call functions defined by the model without having to know the implementation details of how the model implements the function. In other words, the controller doesnt have to know that the model uses commands to implement its functionality.
class CloudDiagram : public CObject, public MvcPresentationModel_T<MvcVisualPart> { // Operations public: virtual void AddCloud(CPoint ptLog) { Do(new AddCloudCommand(this, ptLog)); }
Notice that were using the Do() member function defined by the presentation model. This is the convenience function that executes and logs a given command. You can override Do() if you dont want to log the command. 29.Add the menu handlers required to initiate the undo and redo operations. These functions should delegate to the controller and transaction model.
void CloudController::OnEditUndo() { GetDiagram()->Undo(); } void CloudController::OnUpdateEditUndo(CCmdUI* pCmdUI) { pCmdUI->Enable(GetDiagram()->PeekUndo() != NULL); } void CloudController::OnEditRedo() { GetDiagram()->Redo(); } void CloudController::OnUpdateEditRedo(CCmdUI* pCmdUI) { pCmdUI->Enable(GetDiagram()->PeekRedo() != NULL)); }
95
www.roguewave.com
WHITE PAPER
Conclusion
Although the Microsoft Foundation Classes provide developers with a more efficient method for designing applications, the libraries do not include help for many of the powerful, innovative features you want for todays applications. Objective Toolkit fills the holes in MFC to provide Windows software engineers with more than 60 drop-in components that address areas not covered by MFC. Objective Toolkit solves some of the most complex problems that Windows application engineers face. Although many of the solutions seem complex, they are easy to use in your MFC applications. Whether you are an architectural-level engineer or a beginner, we believe that Objective Toolkit will save you days, weeks and even months of valuable development time that you can now allocate to applicationspecific logic. This report has focused on the main features and benefits of Objective Toolkit. You may also have questions about how this product would function in your own development environment. If you would like to learn more about how Objective Toolkit can help your business reduce its software development costs, e-mail sales@roguewave.com, visit http://www.roguewave.com/products/stingray.php or call any of the Rogue Wave offices listed on the back cover of this report.
Rogue Wave Software maintains local offices worldwide. For additional contact information, visit: http://www.roguewave.com/contact/
CORPORATE HEADQUARTERS Toll-free: (800) 487-3217 E-mail: sales@roguewave.com www.roguewave.com UNITED KINGDOM Rogue Wave Software UK Limited Dukesbridge House 23 Duke Street Reading RG1 4SA Phone: +44 (0)8450 549950 Fax: +44 (0)8450 549951 E-mail: sales@roguewave.co.uk
GERMANy Rogue Wave Software GmbH Robert-Bosch-Str. 5, Haus C D-63303 Dreieich, Germany Phone: +49 6103 59 34 0 Fax: + 49 6103 3 69 55 E-mail: sales@roguewave.de FRANCE Rogue Wave Software S.A.R.L. Immeuble Le Wilson 1 - 70, Avenue du Gnral de Gaulle F-92058 PARIS LA DEFENSE Cedex Tl. : +33 (0)1 46 93 94 20 Fax : +33 (0)1 46 93 94 39 E-mail: sales@roguewave.fr
Copyright 2009, Rogue Wave Software, Inc. All Rights Reserved. Rogue Wave is a registered trademark of Rogue Wave Software, Inc. Stingray is a trademark of Rogue Wave Software, Inc. All other trademarks are the property of their respective owners