Stingray 2006.2 Objective Grid ActiveX UserGuide
Stingray 2006.2 Objective Grid ActiveX UserGuide
Version 11.0.0
THIS MANUAL
Copyright 2006 Quovadx, Inc. All Rights Reserved. Rogue Wave and Stingray are registered trademarks of Quovadx, Inc. in the United States and other countries. All other trademarks are the property of their respective owners.
For information on technical support, please see the Technical Support section in Chapter 1 of this User's Guide.
Contents
1Chapter 1 Introduction to Objective Grid for ActiveX
1.1 1.2 1.3 Welcome to Objective Grid for ActiveX 1 Product Features 2
1.2.1 Frequently Asked Questions 3
Supported Platforms 4
1.3.1 Objective Grid for ActiveX (OGX) and VC.NET 4 1.3.2 OGX and XP 5
1.7 1.8
PART I
USERS GUIDE
Contents iii
2.2.1 Creating a User Interface 14 2.2.2 Methods, Properties, and Events 22 2.2.3 Adding Code 23 2.2.4 Role of Objects 26
2.3 2.4
Additional Tasks 37
2.4.1 Changing Text in Column Headers 37 2.4.2 Assigning a Value to a Range of Cells 37 2.4.3 Changing behavior of the Enter key 37
3Chapter 3 Controls
3.1 3.2 3.3 3.4 Introduction 39 Control Types 39 Code Sample 43 Customization 54
3.4.1 Run time Changing of Dropdown List 54 3.4.2 Activate and De-activate Scrollbars 56
5.2
iv
Important Terms 74
ODBC control 75 Objective Grid DAO control 83 Objective Grid ADO control 83
5.5.1 Steps 84
6.2 6.3
6.4 6.5
7Chapter 7 Samples
7.1 7.2 7.3 7.4 Introduction 101 Internet (CAB) Sample 102 Visual Basic Samples 102 Troubleshooting 106
7.4.1 CAB Sample 106 7.4.2 General 107
8Chapter 8 Redistribution
8.1 8.2 Introduction 109 Naming Conventions 110
8.3
9Chapter 9
Objects
9.1 9.2 9.3 Architecture Introduction 113 Parameter object 117 Style Object 117
9.3.1 Style object creation 118 9.3.2 Setting style properties 118 9.3.3 Style Inheritance 119 9.3.4 User attributes 121 9.3.5 Style Constants/Modes 121 9.3.6 Virtual mode 122 9.3.7 Style Implementation 123 9.3.8 Virtual Functions 124
9.4
Range List 129 Brush Object 129 Data Object 130 Font Object 130 Stylesmap 131
9.10 Print info Object 132 9.11 Properties Object 132 9.12 State Object 132
vi
PART II
17Chapter 17 Interfaces
17.1 Introduction 189 17.2 Brief Introduction to COM 189
17.2.1 Evolution of COM from Dynamic Link Library (DLL) architecture 190 17.2.2 OLE/COM References 192
viii
17.3 Adding Controls to the Tool Palette 192 17.4 Interfaces 193
ix
CHAPTER 1
1.1
OGX controls are an object-oriented wrapper (COleControl-based set of classes) around the Stingray Objective Grid C++ class library. These classes wrap the functionality of the grid as stand-alone ActiveX components. Since these classes are COleControl-based, you can derive new classes from them or modify the existing classes, just as you can with any other C++ class library. OGXs functionality is similar to that of Objective Grid for MFC. In fact, most APIs have a one-to-one correspondence. The object-oriented nature of Objective Grid is also largely carried over to the ActiveX controls, due to the use of Automation objects (an object model comprised of these objects). In short, if you have previous experience with Objective Grid for MFC, you will only need a few minutes to get going with the ActiveX version. If you do not have previous experience with the grid in C++, this Users Guide will have you up and running quickly. For details on the OGX Automation Objects, please refer to Chapter 9, Objects.
1.2
Product Features
Objective Grid for ActiveX provides ActiveX developers with a feature-rich grid control (a user interface component that displays data in rows and columns). OGX can be used as a view or as a window, enabling the user to embed grid controls in other windows, such as dialogs and pop-ups. The OGX control comes in a variety of configurations: Base grid (without database support) Tab control (with and without database support) ADO grid DAO grid ODBC grid Popular features of Objective Grid for ActiveX include: Granular implementation - The Build Wizard and Control Factory Wizard give you the ability to exclude unwanted functionality from the compiled OGX libraries. Wide range of built-in cell types - OGX offers more than 25 cell types, ranging from simple edit controls to formatted date-time and currency controls. Database connectivity - OGX ships with built-in support for ODBC, DAO, and ADO databases. It also provides the ability to create new browser grids for unsupported or proprietary databases. Undo/Redo - OGX fully supports undo/redo for built-in grid operations, including transactions and rollbacks. The architecture can be easily extended to include application-specific operations. Printing and print preview - OGX supplies print and print preview support for grids. Find/Replace - OGX provides support for finding and replacing cell data. Cut/Copy/Paste - OGX supports cut/copy/paste using the Windows clipboard as well as an internal direct cut/copy/paste. Pre-built style dialogs - A set of pre-built dialog boxes is supplied to allow your end user access to the styles architecture. These dialogs enable the end user to make changes to the grids appearance. Object-oriented cell architecture - OGX uses a control sharing architecture in which each type of cell shares a single control with all similar types. Cell
data is stored separately from the cell control. This system saves space and resources. Excel-like interface - OGX enables the programmer to mimic many of the user interface features in Microsoft Excel. Floating and merged cells - OGX provides support for both floating and merged cells. Floating cells are cells that automatically grow over adjacent cells to accommodate text too large for a single cell. Merged cells are adjacent cells that contain the same value and are drawn as a single large cell. Formula support - The OGX formula engine ships with more than 200 built-in worksheet functions. If that's not enough, the formula engine can be extended with your own custom worksheet functions. Objective Grid for ActiveX has Excel-like tabs. Ships with full source code - OGX ships with full C++ source code.
1.2.1
Before we proceed any further, let us answer some of the most frequently asked questions about this product. Do I have to write any additional C++ code to use Objective Grid for ActiveX? No, you do not have to write any additional C++ code to be able to use the OG ActiveX controls. The base grid control and the DAO/ODBC/ADO controls can be compiled out of the box. We also ship release builds of all these controls. You can use these out of the box without the need to do any C++ builds. Only if you need to make any modifications to the control do you need to use the OG ActiveX source inside Visual C++ and either modify or derive from the provided classes. Part II, Visual C++ Users Guide, contains advice for C++ programmers using this product. Does OG for ActiveX ship with the full source code? Yes, Objective Grid for ActiveX ships with full source code. Do I need the Objective Grid MFC libraries? Yes, you will need to have the Objective Grid MFC libraries. Remember, OG ActiveX is a wrapper around Objective Grid and, therefore, needs the Objective Grid libraries to be functional.
Does OGX implement a workbook interface? OG ActiveX implements a full workbook interface, similar to that of Microsoft Excel. The worksheet tabs can be data bound with ODBC, DAO, or ADO or they can be unbound. What types of data binding does Objective Grid for ActiveX implement? OG ActiveX implements ODBC, DAO, and ADO-based data binding, based on the functionality provided by Objective Grid class library. The ADO control can also be used for data access across the Internet. Does Objective Grid for ActiveX require previous OLE/ActiveX experience? All of this functionality is available to you even if you have no previous experience with OLE/ActiveX. You do not have to be familiar with any OLE concepts or have any previous OLE experience to use the ActiveX version. We have included all the required information in the OG ActiveX Users Guide. If you expect to significantly modify the functionality provided by the ActiveX version, then you will require some exposure to basic OLE control programming using MFC. Most books on MFC programming have a few chapters on this and any such short introduction should be all you need. Section 17.2, Brief Introduction to COM, discusses some of the basic aspects of the Component Object Model (COM) and Object Linking and Embedding (OLE). It also suggests some essential reading that may give you a better understanding of this product. The brief introduction to OLE and COM is designed to provide a starting point for those OG ActiveX users who have had little or no exposure to COM.
1.3
Supported Platforms
For a list of supported operating systems and compilers, see:
http://www.roguewave.com/support/matrices/
1.3.1
The procedures for building and using VC.NET OGX components are the same as those for building and using VC6 OGX components. The solution files are required for building OGX. OGX components that are built under VC.NET can be still used under VB6 and VB.NET.
1.3.2
OGX and XP
Objective Grid for ActiveX can be compiled under both VC6 and VC.NET on Windows XP. On Windows XP, OGX can take advantage of XP visual styles. All the samples for OGX in VB.NET are enabled for XP visual styles. This is done by including a manifest file in the application. The visual styles applied to controls depend on the Windows theme enabled in the system.
NOTERefer to Microsoft Windows XP and .NET documentation, for further details on using visual styles and enabling them in applications.
For OGX to function correctly when visual styles are enabled, VB and VC applications should use the Unicode versions of the OGX components. Objective Grid (OG) subclasses the common controls and uses them. When the application points to comctrl32.dll version 6.0, the subclassed control will work only with the Unicode version of the libraries. Since OGX control classes wrap the functionality of the OG as stand-alone ActiveX components, the above-mentioned limitation of OG is also applicable to OGX. However, if the windows themes are disabled on the windows XP machine, the ANSI versions of the OGX components can be used without any problemsif the manifest file is removed from the applications.
1.4
Installed Files
When installation is complete you should see the following directories installed under the installation directory that you selected: Table 1 Installation Directories Directory Appwiz Basectl Definition Contains appwiz.awx. This directory contains all the Base grid control code. The base grid control implements all the core grid functionality in the OLE control. This folder contains the OG Designer executables as well as folders (ado, dao, db, and nodb) into which the pre-built ocxs, supporting dlls, and lic files are installed. All the controls have to be registered (using regsvr32.exe) before they can be used.
Bin
Table 1 Installation Directories (Continued) Directory Build Docs Links ogadoobjects ogdaoobjects ogdbobjects ogobjects Redistribution Definition Contains project, workspace, and make files. This directory contains the OG ActiveX User's Guide and other documentation. This directory contains important and useful web page links. Supporting ADO objects DLL. Supporting DAO objects DLL. Supporting ODBC objects DLL. This directory has the implementation of the supporting automation objects DLL. A .dep file (ogocxgrd.dep) is provided in the Redistribution folder of your installation for use with the VB wizard. This directory contains the samples that ship with the product. There are sample projects for use of the OG ActiveX controls in both Visual Basic and Visual C++. Several useful batch files and utilities are included in this directory.
samples
Tools
1.5
Product Documentation
Documentation is located in the Docs subdirectory of your Objective Grid for ActiveX directory. The following documents are available: User's Guide - This manual. The Objective Grid for ActiveX User's Guide is an introductory how-to manual for Objective Grid for ActiveX. Its main focus is to introduce the user to Objective Grid for ActiveX and to provide a foundation for using Objective Grid for ActiveX out-of-the-box. There are several tutorials included to help new Objective Grid for ActiveX users learn how to create Objective Grid for ActiveX applications quickly. This document is available in both HTML Help (ogxug.chm) and Portable Document Format (ogxug.pdf) formats.
Class Reference - The Objective Grid for ActiveX Class Reference (ogxref.chm, ogx.hlp) is a detailed description of the properties, methods, and events in Objective Grid for ActiveX. Printed documentation - The Objective Grid for ActiveX Users Guide may also be available as a bound printed documentation for an additional charge. For information, please contact the Rogue Wave Sales department by phone at (800) 924-4223 or by e-mail at sales@roguewave.com. Samples - Objective Grid for ActiveX ships with a wide variety of samples (located in the samples subdirectory of your Objective Grid for ActiveX installation directory).
1.6
1.6.1
Type Conventions
Throughout this document, a set of typographical conventions are used to define elements and references to Objective Grid for ActiveX items. Familiarity with these conventions will help your understanding of the topics covered. Table 2 Documentation conventions Example
myfunc()
Description Library routines have parenthesis '()' as a suffix. Class names declared in the grid component contain the prefix 'CGX'. Library routines declared in the grid component begin with 'GX'. Definitions and constants begin with 'GX_' Words in italics indicate placeholders for information you must supply, such as a variable or parameters for methods. Courier font is used for code examples. A column or row of three dots (also known as an ellipsis) indicates that part of an example program has been intentionally omitted. Words in small caps indicate names of keys on the keyboard. A plus sign (+) between two key names indicates that you should hold down the first key while pressing the second. Bold font is used for menu commands.
CGXGridView
GXPatB
GX_UPDATENOW
Expression
ENTER CTRL+ENTER
Menu|Menu Item
1.6.2
Glossary
Following are definitions for several important terms that you will encounter in this Users Guide. Check the index at the back of the book to locate further information about any of these topics. Base Style - Base styles are grid-wide styles that make it possible to group specific kinds of cells and give them similar attributes. The predefined base styles are: rowheader-style, column-header-style and standard-style. A cell in a particular row
8 Objective Grid for ActiveX Users Guide
inherits attributes from its row base style. Row header cells inherit their attributes from row-header-style. Column headers inherit from column-header-style. Standard-style is the base style for all cells in the grid. Cell - Cells display information in the grid. Each cell has a unique coordinate (row, column). Cells are associated with a control and a style object. The control is responsible for handling user events and drawing the information provided through the style object. Control - Controls handle the interface between the end user and the grid. Each cell is associated with a control. The control interprets user events and is responsible for drawing the cell. Control Child - Controls can have small children in the cell. For example, the spin control has an up-arrow button child and a down-arrow button child. The control child is normally a small rectangular area in the parent control's area. Covered Cells - Objective Grid for ActiveX lets you cover cells. This means that one cell can span several other cells. This is very useful for headings in reports. Current Cell - The grid manages the current cell as the user navigates through the grid by clicking or using arrow keys. The current cell lets the user modify the cell's contents through its associated control. The end user can interact directly with the control. Data source - Data source is a general term that can mean either an ODBC/DAO/ ADO query result, a database, or any other external data structure or medium. ODBC-Open DataBase Connectivity-A standard database access. DAO-Data Access Objects-Objects that work with the Jet database engine. DAO are created with Visual Basic. A DAO can be accessed and manipulated by any application using the Jet engine. ADO-ActiveX Data Objects-Will eventually replace DAO. Properties - Properties are settings in the grid that can be modified with pre-built dialogs and can be loaded/archived using the registry/profile. Properties are maintained by the CGXProperties class. Range - A range defines a rectangular area of cells in the grid. A range is specified through a top and bottom row, and left and right columns. Ranges can represent a selection of cells, columns, rows, or all cells. Style - A style contains all the information necessary for formatting a cell. A style consists of several attributes such as text color, borders, control type, and font. Each cell determines the style information at run time and passes this information to the control for drawing the cell.
Workbook - A workbook lets the user switch between several views connected to the same document by clicking on a tab at bottom-left of the window. Worksheet - Worksheet is used to refer to each of the individual views displayed in a workbook.
1.7
1.8
Technical Support
Before contacting Rogue Wave Support Services for the first time, you must register with our support system. To do this, go to http://www.roguewave.com/youraccount/private/register and follow the instructions. If you would like to register an additional product, just use the Back button on your Web browser to return to the registration form. After registering, you may then access the online technical support area with your e-mail address (or Rogue Wave ID) and password. Technical support for Objective Grid for ActiveX products is provided through the Rogue Wave Web site. See our support page (http://www.roguewave.com/ support) for access to the Knowledge Base, the online technical support form, downloadable upgrades, and technical support. Before entering a support request, check the online Knowledge Base. In many cases, your technical support question has already been answered in the Knowledge Base. This valuable resource is provided as a convenience for our customers, and provides technical answers to many frequently asked questions. If you have any difficulty accessing support online, please contact Rogue Wave Support Services at (800) 404-4767 or (303) 545-3205, or send e-mail to support@roguewave.com.
10
PART I
Users Guide
12
CHAPTER 2
2.1
Introduction
Objective Grid for ActiveX is a suite of full-featured ActiveX controls that are specially designed to take full advantage of the ActiveX environment that is provided by advanced ActiveX control containers such as Microsoft Visual Basic and Microsoft office applications. The OG ActiveX controls are listed below, along with a brief description of what makes them unique. Objective Grid base control (ogocxgrd.ocx): This is the control to use if you are interested in plain grid functionality and are not interested in any kind of database support (unbound mode). Objective Grid ADO data control (ogadogrd.ocx): Use this control if you intend to access data with ADO (ActiveX Data Objects). We recommend that you use this approach for new projects. Using ADO has the added advantage that deploying your data over the Web becomes a snap. With the ADO control, samples are provided that let you deploy your data over the Internet with very little programming effort. Objective Grid DAO data control (ogdaogrd.ocx): Use this control to access DAO (Data Access Objects) data sources. Accessing DAO-friendly data sources (such as MS Access) with this control might be faster than using ODBC. Objective Grid ODBC data control (ogdbgrd.ocx): This is the control to use for accessing ODBC (Open DataBase Connectivity) data sources with the grid interface as the front end. This control can bind to an ODBC data source, using very little code. Objective Grid tab control (ogocxtab.ocx): This control gives you the default Excel workbook look and feel. Multiple grid tabs are supported. Tabs can be easily inserted and removed at run time.
Chapter 2 Quick Start Guide 13
Later in this chapter we will look at using the OGX controls. First, however, well take a look at Visual Basic.
2.2
2.2.1
Microsoft Visual Basic was one of the first development environments to introduce the concept of visual development of Windows applications. Visual Basic ships with its own palette of controls that can be dropped onto a form and then customized for application-specific needs. Figure 1 shows the Toolbox for Visual Basic 7.0.
14
The Toolbox contains many of the common controls used in the Windows environment, including text boxes, labels, buttons, list boxes, and combo boxes. To use one of these controls: Double-click on the control icon in the toolbar and an instance of the control appears on the Windows form. or
Click once on the icon and then place and size the control on the form. After you place an instance of the control on your form, you can proceed to modify the control as you see fit, using the properties provided by the control designer. All this is done without writing code. Once the user interface is complete, you can proceed to interact with the control in more complex ways (using VB code). For example, lets say that we want to have a simple input form that will receive and use input from users. The text box control is capable of receiving text input from the user. The following steps demonstrate how to configure this user interface: 1. Create a new Visual Basic project (CTRL+SHIFT+N). Figure 2 New Project Dialog
2. If the Toolbox is not visible, click Toolbox on the View menu. or Click the Toolbox button on the Standard toolbar to display it.
16
Double-click the text box icon on the VB Toolbox to create an instance of this control.
4. Click the text box to select it and drag to move it around on the form. Drag handles to resize it as necessary. 5. Select the text control and press F4 to display the Properties window (if necessary). 6. Use the Properties window to set the ForeColor property to yellow.
7. Set the BackColor property to blue. The result would be a control on a form that looks like the one in Figure 4.
18
Figure 4 Text input control with ForeColor and BackColor properties set
9. Clear out the Text property of the txtName text box. (This is so the text box will be blank when the form initially appears at run time.) 10. Add a label control to the left of the text box.
A label is also capable of displaying text but, unlike the text box, will not let the user change its contents. 11. Change the labels Caption property to Name: and its TextAlign to TopRight.
20
12. Add another TextBox control to the Windows form. 13. Change the (Name) property to nameStr.
2.2.2
Now we have the user interface required to get the users name. If the name of the text box is txtName, we can write code like this:
Dim str as string 'Declare a string variable str = txtName.text
The preceding code allows us to collect the name entered into the txtName text box by the user in a string variable (str). We can use or store the string in any manner that we choose. (The control can also be bound to a database so that it writes the entered values directly into the database without any work on our part.) The key point is that the text box is seen as an object having some defined properties that the programmer can manipulate at design time and/or run-time (and in some cases the user can manipulate them at run-time). The BackColor, the ForeColor, and the Name are three text box properties, just as the text itself is one of these properties (Text). How the control changes the BackColor when we ask it to do so does not concern us. It is up to the control implementers to take care of such details. We benefit from the higher-level functionality of these controls, because they are very easy to use. More complex controls would also have several methods. Methods are different from properties in that they often cause some action to be taken on the control.
22
Properties describe the control, while methods describe and effect changes to the control. Methods can also change properties. Events are actions of interest that happen to the control. The programmer can choose to react to or ignore them. For example, a button control can have a Click event. The control will call this event (which is often just a function) whenever the user clicks on the button. The programmer can choose to react to this event and display an acknowledgment to the user, for instance. Other events associated with a command button control include: Leave and Enter. In addition to these, other events associated with a text box include: DoubleClick (double-click), TextChanged, Validated, and KeyPress.
2.2.3
Adding Code
Lets try this out by creating an event procedure. 1. Change the name of your form to frmGetUserInfo and the forms Caption to User Information.
NOTECaption and Text properties can have spaces or even be blank, but the name of a control cannot.
2. If the Solution Explorer window is not already visible, from the View menu choose Solution Explorer (or press CTRL+R). 3. Click the View Code button in the Project Explorer window to view the Code Window.
or Double-click on the form (or on a control on it) to display the Code Window. or Choose Code from the View menu.
24
4. Click the down-arrow to the right of the Object box. The drop-down menu contains a list of the available objects. Choose the txtName object:
5. Click the down-arrow to the right of the Procedure box. The drop-down menu contains a list of the available events. Choose the DoubleClick event.
7. Run the program (F5). 8. Type a name in the text box. 9. Double-click on the text box.
This simple program accepts user input and displays it back in another TextBox control. To make the program more transparent to users, you could also choose to have users click a command button labeled Push Here (instead of having them double-click the text box) to display the output. You could also write code that would write the data to a data storage unit (a file or a database, for example).
2.2.4
Role of Objects
We take a brief aside here to explain the use of objects in a scenario like this. While properties, methods and events form a powerful interface that allows us to benefit from the functionality of controls, sometimes working with them can be difficult. This is especially true with complex controls. Take a grid control, for
26
example. If we wanted to set the ForeColor, the BackColor, and the Font of cell 1, 1 in a grid control, we could have methods like these:
NOTEThese methods (SetForeColor, SetBackColor, SetFont, SetStyle) are used for demonstration purposes only; they are not real methods. gridControl.SetForeColor(1,1,color1) gridControl.SetBackColor(1,1,color2) gridControl.SetFont(font1)
Now if cell 2, 2 were to have the same BackColor and Font but a different ForeColor, then we would have to call these methods as follows:
gridControl.SetForeColor(2,2,color3) gridControl.SetBackColor(2,2,color2) gridControl.SetFont(font1)
As you can see, these calls can add up very quickly. An object-oriented grid like Objective Grid provides objects that define the cells. With objects, things become much simpler. For example, Objective Grid defines a style object that defines these properties and more. Now the code will look like:
style.SetFont(font1) style.SetForeColor(color1) style.SetBackColor(color2)
And we can tell the grid to use this style object to set these values.
gridControl.SetStyle(1,1, style)
This sort of object-defined behavior can make things much simpler when dealing with complex controls. Most controls on the market today do not offer this kind of interface.
2.3
2. To add the OGX control to your project, click Customize Toolbox on the Tools menu.
NOTEYou can also right-click on the Toolbox and choose Customize Toolbox to display the Customize Toolbox window.
28
3. Scroll down until you locate the Objective Grid components (in alphabetical order) and select Objective Grid.
NOTEType O to jump there quickly.
30
4. Click OK. The icon for the Objective Grid component appears on your VB.NET toolbox. Figure 11 GridocxCtrl Icon
5. Click the Gridocx icon. 6. Click your Windows form and drag the cross-hair mouse pointer to form a rectangle, thereby creating and sizing an instance of an OGX control on your VB form. (Or double-click to place a grid of a default size. Then you can move it around and resize it.) 7. Change the Name property for OGX control to Gridocx1. 8. Choose Code from the View menu.
9. Click the down-arrow to the right of the Class Name. The drop-down menu contains a list of the available objects. 10. Choose (Base Class Events).
11. Click the down-arrow to the right of the Method Name box. This drop-down contains a list of available events. 12. Choose Load Event, and replace the code with the following:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim range As Object range = Gridocx1.CreateRangeObject() range.InitRange(1,1,1,1) Dim style As Object style = Gridocx1.CreateStyleObject() style.SetValue("Hello") 'now call SetStyleRange to apply this value to range (1,1) Gridocx1.SetStyleRange(range,style, GRIDOCXLib.constantdefs.gxCopy)
'now change the values range.InitRange(2,1,2,1) 'change the value in the style object
32
style.SetValue("World") 'call SetStyleRange to apply this value to range (2,1,2,1) Gridocx1.SetStyleRange(range,style, GRIDOCXLib.constantdefs.gxCopy) End Sub
13. Press F5 to run the project. 14. Try entering text in various cells. Use the Tab key or the arrow keys to move from cell to cell.
NOTEWhen you are editing a cell, the default action is for the Enter key to create a new line in the active cell. This can be changed using the SetAllowEnter and SetEnterkeyAction methods of the grid param object. See Section 2.4.3 for details.
As you can see from the above exercise, the API is very simple and makes use of the previously discussed style object, as well as the range object. We merely set the range to apply the style to, change the style object to reflect the changes that we want made, and then apply this style to this range with a call to SetStyleRange(). This simple interface can be used to change every aspect of the cell. One of the objectives of providing an API such as this is to maintain consistency between settings. For example, much of the code above can be reused in changing say the interior color of the cells. Shown below is some sample code that changes the interior color for cells A1 and B1. Replace the current Form1_Load() code with the following:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs)Handles MyBase.Load Dim range As Object range = Gridocx1.CreateRangeObject() range.InitRange(1,1,1,1) Dim style As Object style = Gridocx1.CreateStyleObject() style.SetValue("Hello") style.SetInterior(RGB(0,255,0))
'now call SetStyleRange to apply this value to this range (1,1) Gridocx1.SetStyleRange(range,style, GRIDOCXLib.constantdefs.gxCopy)
'now change the values range.InitRange(2,1,2,1) 'change the value in the style object style.SetValue("World") style.SetInterior(RGB(255,0,0)) 'call SetStyleRange to apply this value to range (2,1,2,1) Gridocx1.SetStyleRange(range,style, GRIDOCXLib.constantdefs.gxCopy) End Sub
As you can see from the above code, the interface for making the calls remains the same. All that was required to change the cells colors was one additional call to SetInterior() on the style object. Now that we understand the basics of using the grid control, lets add a different cell type (a combo box).
34 Objective Grid for ActiveX Users Guide
2.3.1
Cell Types
The default cell type in an Objective Grid control is an Edit control. Typically this lets you edit a cell just as a plain edit control would. Objective Grid ships with several other controls. Depending on your needs you will be using one or more of these child controls as cell styles. To illustrate using these controls, lets add a simple ComboBox that displays a choice list and lets users select from this list. 1. Make sure that your grid is large enough so that you can see cell C3.
NOTEResize both the form and the grid, if necessary.
4. Test the combobox. (You might want to resize column C.) From the code above, we can see two new calls that are applied on the style object:
SetControl() and SetChoiceList(). SetControl() sets the control type for the cell and the style to be a combobox. Each of the control types have predefined constants. Just making this call changes the control type that is used for this cell. GX_IDS_CTRL_CBS_DROPDOWN is the setting used to create an MFC Style Combo Box. NOTESetChoiceList() is commonly used for all controls that need more than one value. With the exception of the PushButton control, with which you must use SetChoiceList() instead, one value can be set with SetValue().
As discussed in detail in Chapter 3, there are many more OGX cell types to choose from. A comprehensive list of all of the available Settings for all control types is documented in Table 4 in Chapter 3 and under the SetControl() method information in the OGX Class Reference (ogxref.chm).
36
2.4
Additional Tasks
This section contains a few addtional things you might want to try as you experiment with the grid. Refer to Chapter 10, Advanced Topics, for more advanced How-To information.
2.4.1
You can remove the headers completely using the following code:
Gridocx1.GetGridParam.GridSetNumberedColHeaders(False)
You can treat the column/row headers like any other cell in the grid. In order to change the text of the headers, you can use the SetStyleRange method of the Grid object. The following code sets the text for the first two column headers:
Dim range As Object Dim style As Object style = Gridocx1.CreateStyleObject() range = Gridocx1.CreateRangeObject() style.SetValue(Name) range.InitRange(1,0,1,0) Gridocx1.SetStyleRange(range,style, GRIDOCXLib.constantdefs.gxOverride) style.SetValue("Customer ID") range.InitRange(2,0,2,0) Gridocx1.SetStyleRange(range,style, GRIDOCXLib.constantdefs.gxOverride)
2.4.2
2.4.3
When you are editing a cell, the default action is for the Enter key to create a new line in the active cell. This can be changed using the SetAllowEnter method (set to
False) of the grid style object and the SetEnterKeyAction method of the grid param object. The GX_DOWN parameter in the following code will cause the active cell to move down one row after the Enter key is pressed: Dim param As Object Dim prop As Object Dim style As Object Dim range As Object style = Gridocx1.CreateStyleObject range = Gridocx1.CreateRangeObject range.SetTable() style.SetAllowEnter(False) Gridocx1.SetStyleRange(range, style, GRIDOCXLib.constantdefs.gxOverride) param = Gridocx1.GetGridParam() param.SetEnterKeyAction(GRIDOCXLib.GXDirectionType.GX_DOWN)
Table 3 lists all of the possible settings for changing the behavior of the Enter key. Table 3 Settings for Enter Key Action Setting 0 GX_UP GX_LEFT GX_RIGHT GX_DOWN Description The current cell will not change. The current cell should move up one row. The current cell should move left one column. The current cell should move right one column. The current cell should move down one row.
38
Controls
THIS CHAPTER INCLUDES: Introduction
Control Types Code Sample Customization
CHAPTER 3
3.1
Introduction
Control types are set using the SetControl method of the OGStyle object. For example:
style.SetControl(GRIDOCXLib.constantdefs.GX_IDS_CTRL_CBS_DROPDOWN)
This chapter summarizes each of the available control types and in many cases includes information on customizing their behavior.
3.2
Control Types
The following table lists all of the possible control types: Table 4 Control Types Control Edit Control Static Text Control Push Button Radio Button Looks Like Setting GX_IDS_CTRL_EDIT
GX_IDS_CTRL_STATIC
GX_IDS_CTRL_PUSHBTN
GX_IDS_CTRL_RADIOBTN
Chapter 3 Controls 39
Edit with Hot Spot Edit Control With Spinner Header Edit with Scrollbar ListBox
GX_IDS_CTRL_HOTSPOT
GX_IDS_CTRL_SPINEDIT
GX_IDS_CTRL_HEADER GX_IDS_CTRL_SCROLLEDIT
GX_IDS_CTRL_LISTBOX
GX_IDS_CTRL_CHECKBOX3D
GX_IDS_CTRL_RADIOBTN3D
GX_IDS_CTRL_COMBOBOX
GX_IDS_CTRL_TEXTFIT
40
Combo Box, Display Choice, Zero Based Combo Box One Based
GX_IDS_CTRL_ZEROBASED_E X
GX_IDS_CTRL_ONEBASED
Combo Box Display Choice, One Based Windows (MFC Style) Combo Box
GX_IDS_CTRL_ONEBASED_EX
GX_IDS_CTRL_CBS_DROPDO WN
GX_IDS_CTRL_CBS_DROPDO WNLIST
GX_IDS_CTRL_CBS_TABBED_D ROPDOWN
Chapter 3 Controls 41
Table 4 Control Types (Continued) MFC Style Tabbed Drop Down List GX_IDS_CTRL_CBS_TABBED_D ROPDOWNLIST
GX_IDS_CTRL_CHECKLIST_C OMBOBOX
GX_IDS_CTRL_DATETIME
GX_IDS_CTRL_DATETIMENOC AL
42
GX_IDS_CTRL_MASKEDIT
3.3
Code Sample
The following code is used in the gridapp sample to create a number of different controls:
Public Sub Controls_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles ControlsTypes.Click ' initialize some state variables bValidate = False IsOwnerDraw = False IsVirtual = False ' some useful values Dim strTab As String Dim strLinef As String strTab = Chr(System.Windows.Forms.Keys.Tab) strLinef = Chr(10)
'This page displays and demonstrates the usage of some of the more commonly used controls. ' refer styles_click for more details on sharing parameter objects and a step wise explanation of the
Chapter 3 Controls 43
' calls made here and elsewhere Dim b As Boolean Dim range As Object Dim Style As Object Dim strTabbedChoice As String Dim nSelIndex As Short Dim styleStandard As Object If (controlBool = False) Then controlParam = Gridocx1.CreateParamObject() Gridocx1.SetParam(controlParam, False) Gridocx1.InitializeGrid() The parameter object has been created controlBool = True b = Gridocx1.LockUpdate(True) Gridocx1.SetRowCount(0) Gridocx1.SetColCount(0) Gridocx1.SetRowCount(35) Gridocx1.SetColCount(10) Gridocx1.InteriorColor = System.Drawing.ColorTranslator.FromOle(RGB(255, 255, 255)) 'shows the OG controls 'create an object of type Range range = Gridocx1.CreateRangeObject() 'All aspects of the cells can be manipulated with the style 'objects 'declare an object 'create a CGXStyle object Style = Gridocx1.CreateStyleObject()
range.InitRange(1, 1, 1, 1) Gridocx1.SetValueRange(range, "Cell Types", 2) range.InitRange(1, 2, 1, 2) Gridocx1.SetValueRange(range, "Edit control", 2) 'call setStyleRange to set the style object on this range Style.SetControl(GRIDOCXLib.constantdefs.GX_IDS_CTRL_EDIT) Style.SetValue("Edit control") range.InitRange(3, 2, 3, 2) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
44
range.InitRange(1, 3, 1, 3) Gridocx1.SetValueRange(range, "Static control", 2) 'call setStyleRange to set the style object on this range Style.SetControl(GRIDOCXLib.constantdefs.GX_IDS_CTRL_STATIC) range.InitRange(3, 3, 3, 3) Style.SetValue("Static Text") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 4, 1, 4) Gridocx1.SetValueRange(range, "Push Button", 2) 'call setStyleRange to set the style object on this range Style.SetControl (GRIDOCXLib.constantdefs.GX_IDS_CTRL_PUSHBTN) range.InitRange(3, 4, 3, 4) Style.SetChoiceList("Push") Style.SetInterior(GetSysColor(COLOR_BTNFACE)) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 5, 1, 5) Gridocx1.SetValueRange(range, "Radio Button", 2) 'call setStyleRange to set the style object on this range Style.Free() Style.SetControl (GRIDOCXLib.constantdefs.GX_IDS_CTRL_RADIOBTN) range.InitRange(3, 5, 3, 5) ' the list of choices for the radio button group Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) Gridocx1.ResizeRowHeightsToFit(range) range.InitRange(1, 6, 1, 6) Gridocx1.SetValueRange(range, "CheckBox", 2) 'call setStyleRange to set the style object on this range Style.Free() Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CHECKBOX) range.InitRange(3, 6, 3, 6) Style.SetValue("CheckBox") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
Chapter 3 Controls 45
range.InitRange(1, 7, 1, 7) Gridocx1.SetValueRange(range, "Edit with Hot Spot", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_HOTSPOT) range.InitRange(3, 7, 3, 7) Style.SetValue("Try me!") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 8, 1, 8) Gridocx1.SetValueRange(range, "Edit Control With Spinner", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_SPINEDIT) range.InitRange(3, 8, 3, 8) Style.SetValue("Try me!") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 9, 1, 9) Gridocx1.SetValueRange(range, "Bitmap Button", 2) 'Bitmap button 'Dim pathStr As String 'Please hard code the path if this call fails due to an incorrect path 'pathStr = StingRegOcx1.GetInstalledPath + "\samples\images\" ' we use two bitmaps one for regular position and one to be displayed when clicked on ' Both can be the same Gridocx1.RegisterBitmapControl(2000, "..\sad.bmp", "..\happy.bmp", CStr(0)) Style.SetBitmap(2000) range.InitRange(3, 9, 3, 9) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
range.InitRange(1, 10, 1, 10) Gridocx1.SetValueRange(range, "Edit with Scrollbar", 2) 'call setStyleRange to set the style object on this range Style.SetControl(
46
GRIDOCXLib.constantdefs.GX_IDS_CTRL_SCROLLEDIT) range.InitRange(3, 10, 3, 10) Style.SetValue("Try me! As you can see I have a scrollbar") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) Gridocx1.SetRowHeight(10, 10, 55) range.InitRange(1, 11, 1, 11) Gridocx1.SetValueRange(range, "ListBox", 2) call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_LISTBOX) range.InitRange(3, 11, 3, 11) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) Gridocx1.ResizeRowHeightsToFit(range)
range.InitRange(1, 12, 1, 12) Gridocx1.SetValueRange(range, "3D CheckBox", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CHECKBOX3D) range.InitRange(3, 12, 3, 12) Style.SetValue("Check Me!") Style.SetChoiceList("3D checkbox") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
range.InitRange(1, 13, 1, 13) Gridocx1.SetValueRange(range, "3D Radio Button", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_RADIOBTN3D) range.InitRange(3, 13, 3, 13) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) Gridocx1.ResizeRowHeightsToFit(range) range.InitRange(1, 14, 1, 14) Gridocx1.SetValueRange(range, "Combobox No Text Fit", 2)
Chapter 3 Controls 47
'call setStyleRange to set the style object on this range 'We call free whenever we have to re initialize the style Style.Free() Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CBS_DROPDOWN) range.InitRange(3, 14, 3, 14) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("Choice one") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 15, 1, 15) Gridocx1.SetValueRange(range, "Combobox Text Fit", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_TEXTFIT) range.InitRange(3, 15, 3, 15) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("Choice two") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) Gridocx1.ResizeRowHeightsToFit(range)
range.InitRange(1, 16, 1, 16) Gridocx1.SetValueRange(range, "ComboBox Zero Based", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_ZEROBASED) range.InitRange(3, 16, 3, 16) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("1") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 17, 1, 17) Gridocx1.SetValueRange(range, "ComboBox Zero Based Ex", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_ZEROBASED_EX) range.InitRange(3, 17, 3, 17) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("1")
48
Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 18, 1, 18) Gridocx1.SetValueRange(range, "ComboBox One Based", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_ONEBASED) range.InitRange(3, 18, 3, 18) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("1") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 19, 1, 19) Gridocx1.SetValueRange(range, "ComboBox One Based Ex", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_ONEBASED_EX) range.InitRange(3, 19, 3, 19) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("1") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 20, 1, 20) Gridocx1.SetValueRange(range, "Windows Combobox", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CBS_DROPDOWN) range.InitRange(3, 20, 3, 20) Style.SetChoiceList("choice one" & Chr(10) & "choice two" & Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("Choice one") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 21, 1, 21) Gridocx1.SetValueRange(range, "Windows Drop Down List", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CBS_DROPDOWNLIST) range.InitRange(3, 21, 3, 21) Style.SetChoiceList("choice one" & Chr(10) & "choice two" &
Chapter 3 Controls 49
Chr(10) & "choice three" & Chr(10)).SetValue("0") Style.SetValue("Choice one") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) range.InitRange(1, 22, 1, 22) Gridocx1.SetValueRange(range, "Tabbed Combo box", 2) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CBS_TABBED_DROPDOWN) range.InitRange(3, 22, 3, 22)
' line 1: On the same line choices are seperated by tabs strTabbedChoice = "A01" & strTab & "FISH" & strTab & "One" & strLinef strTabbedChoice = strTabbedChoice & "A02" & strTab & "Pork" & strTab & "Two" & strLinef strTabbedChoice = strTabbedChoice & "A03" & strTab & "Beef" & strTab & "Three" & strLinef strTabbedChoice = strTabbedChoice & "A04" & strTab & "Prawn" & strTab & "Four" & strLinef strTabbedChoice = strTabbedChoice & "A05" & strTab & "Yum!" & strTab & "Five" & strLinef
Style.SetChoiceList(strTabbedChoice) Style.SetValue("Choice one") ' here is a good place to illustrate user attributes ' ' ' ' ' ' ' ' ' ' ' ' ' ' Reason for having user attributes: Throughout the grid code we have been careful to provide a consistent programming interface For example the value of a string in a text box is also accessed and set with Set/GetValue and so is the value of a checkbox though both are different in more ways than one This does not however hold good always and with more complex control there have to be other variables that define aspects of the control User attributes provide this interface. They are specific to each control, yet they can be accessed through a consistent programming interface
' In the case of the tabbed combo box, we have two user ' attributes that we use here ' GX_IDS_UA_TABLIST_KEYCOL: Denotes the column that will
50
' be used as the value (Get/SetValue will deal with this) GX_IDS_UA_TABLIST_TEXTCOLL Denotes the column that will be displayed. This is usually for the benefit of the user For example all of the above meats have their own codes. But to the user these are displayed with the descriptive name in column 1
Style.SetUserAttribute( GRIDOCXLib.constantdefs.GX_IDS_UA_TABLIST_KEYCOL, 0) Style.SetUserAttribute( GRIDOCXLib.constantdefs.GX_IDS_UA_TABLIST_TEXTCOL, 1) Style.SetValue("A01") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) ' tabbed MFC combobox range.InitRange(1, 23, 1, 23) Gridocx1.SetValueRange(range, "MFC Style Tabbed Combo box", GRIDOCXLib.constantdefs.gxCopy) 'call setStyleRange to set the style object on this range Style.SetControl( GRIDOCXLib.constantdefs. GX_IDS_CTRL_CBS_TABBED_DROPDOWNLIST) range.InitRange(3, 23, 3, 23) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
' check list combobox range.InitRange(1, 24, 1, 24) Gridocx1.SetValueRange(range, "Check List Combo box", GRIDOCXLib.constantdefs.gxCopy) ' declare an integer and set the bits ' Selection for this special control is through bitwise ' flags ' In this case to set the first two entries we have to set ' the lower two bits as shown below nSelIndex = (&H1S Or &H2S) 'call setStyleRange to set the style object on this range Style.SetChoiceList("State Choice" & strLinef & "South Carolina" & strLinef & "Georgia" & strLinef & "Alabama" & strLinef & "North Carolina") Style.SetValue(nSelIndex) Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CHECKLIST_COMBOBOX) range.InitRange(3, 24, 3, 24)
Chapter 3 Controls 51
Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) ' the date time control complete with drop down calendar range.InitRange(1, 25, 1, 25) Gridocx1.SetValueRange(range, "Date time control with calendar", GRIDOCXLib.constantdefs.gxCopy)
'call setStyleRange to set the style object on this range ' set the date Style.SetValue("4/5/71") Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_DATETIME) range.InitRange(3, 25, 3, 25) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) ' we can also use this control in several formats and ' without the calendar ' the date time control without drop down calendar range.InitRange(1, 26, 1, 26) Gridocx1.SetValueRange(range, "Date time control without calendar", GRIDOCXLib.constantdefs.gxCopy) 'call setStyleRange to set the style object on this range ' set the date Style.SetValue("4/5/71") Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_DATETIMENOCAL) range.InitRange(3, 26, 3, 26) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride) ' the currency control with calculator range.InitRange(1, 27, 1, 27) Gridocx1.SetValueRange(range, "Currency control", GRIDOCXLib.constantdefs.gxCopy) 'call setStyleRange to set the style object on this range ' set the date Style.SetValue("100") Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CURRENCY) ' the number of fractional digits allowed Style.SetUserAttribute( GRIDOCXLib.constantdefs.GX_IDS_UA_CURNUMFRACT, "6") ' the number of decimal digits allowed
52
Style.SetUserAttribute( GRIDOCXLib.constantdefs.GX_IDS_UA_CURNUMDECIMALS, "6") ' the seperatot betwen two groups Style.SetUserAttribute( GRIDOCXLib.constantdefs.GX_IDS_UA_CURSEP, " .") ' the monetary symbol Style.SetUserAttribute( GRIDOCXLib.constantdefs.GX_IDS_UA_CURMON, "$") ' this user attribute controls several aspects of the ' currency control ' Please refer to the documentation for more details Style.SetUserAttribute(\ GRIDOCXLib.constantdefs.GX_IDS_UA_CURRENCYDEF, "11 1") ' there are user attributes for controlling the number of ' decimals and fractions etc range.InitRange(3, 27, 3, 27) Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
'Masked edit control range.InitRange(1, 28, 1, 28) Gridocx1.SetValueRange(range, "Masked Edit control", GRIDOCXLib.constantdefs.gxCopy)
range.InitRange(3, 28, 3, 28) Style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_MASKEDIT) 'set the mask and the prompt 'SS number mask, Prompt Style.SetMask("###-##-####", "_") Style.SetValue("999999999") Gridocx1.SetStyleRange(range, Style, GRIDOCXLib.constantdefs.gxOverride)
Gridocx1.SetColWidth(3, 3, 150)
Chapter 3 Controls 53
range.SetTable() Gridocx1.ResizeRowHeightsToFit(range) 'make the bitmap button a little bigger Gridocx1.SetRowHeight(9, 9, 75) ' some fancy colors for the table styleStandard = Gridocx1.StandardStyle() styleStandard.SetTextColor(RGB(0, 0, 255)) Gridocx1.LockUpdate(b) Gridocx1.RedrawGrid( GRIDOCXLib.constantdefs.GX_INVALIDATE) Else ' reuse the parameter object that we created earlier Gridocx1.SetParam(controlParam, False) Gridocx1.RedrawGrid( GRIDOCXLib.constantdefs.GX_INVALIDATE) End If End Sub
3.4
Customization
3.4.1 Run time Changing of Dropdown List
To change the content of the drop-down list, you need to use the SetChoiceList method of the style object and re-apply the style to the cell that contains the DropDownList control. The following code demonstrates how to add an additional item to the choicelist at run time. It also shows how you can display sorted dropdown lists.
Dim ChoiceList(20) As String Dim ChoiceString As String Dim NumberOfChoices As Integer Private Sub Gridocx1_OnGridInitialUpdate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Gridocx1.OnGridInitialUpdate Dim n As Integer ChoiceList(0) = "aa" ChoiceList(1) = "bb" 54 Objective Grid for ActiveX Users Guide
ChoiceList(2) = "cc" NumberOfChoices = 2 ChoiceString = "" For n = 0 To NumberOfChoices ChoiceString = ChoiceString & ChoiceList(n) + Chr(10) Next n SetListOfChoices() End Sub Private Sub SetListOfChoices() Dim style As Object Dim range As Object style = Gridocx1.CreateStyleObject range = Gridocx1.CreateRangeObject style.SetControl( GRIDOCXLib.constantdefs.GX_IDS_CTRL_CBS_DROPDOWNLIST) range.InitRange(1, 1, 1, 1) style.SetChoiceList(ChoiceString).SetValue("aa") Gridocx1.SetStyleRange(range, style, GRIDOCXLib.constantdefs.gxOverride) End Sub Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Dim n As Integer ChoiceString = "" NumberOfChoices = NumberOfChoices + 1 ChoiceList(NumberOfChoices) = Text1.Text For n = 0 To NumberOfChoices ChoiceString = ChoiceString & ChoiceList(n) + Chr(10) Next n SortItems() End Sub Private Sub Dim flag Dim temp Dim n As Do flag = False SortItems() As Boolean As String Integer
Chapter 3 Controls 55
For n = 0 To NumberOfChoices - 1 If ChoiceList(n) > ChoiceList(n + 1) Then temp = ChoiceList(n) ChoiceList(n) = ChoiceList(n + 1) ChoiceList(n + 1) = temp flag = True End If Next n Loop While flag = True ChoiceString = "" For n = 0 To NumberOfChoices ChoiceString = ChoiceString & ChoiceList(n) + Chr(10) Next n SetListOfChoices() End Sub
3.4.2
The control method SetScrollBarMode bar, mode allows you to control the visibility of the scrollbars in the grid. Here, bar is 0 (SB_HORZ), 1 (SB_VERT) or 3 (SB_BOTH). The mode is either 0 (disabled and hidden) or 1 (enabled and visible).
Gridocx1.SetScrollBarMode(3,0) Gridocx1.SetScrollBarMode(1,1) 'disable both bars 'enable vertical scrollbar
56
CHAPTER 4
4.1
Introduction
In most cases, using grid tab controls is the same as using regular grids. There are two main differences between the tab control and the regular OGX control. The tab control's methods and properties are always delegated to the Active Tab. This Active Tab need not be the tab with which the user is interacting. You have to set the Active Tab first. The Active Tab can be changed with a call to ActiveTab. You should always call ActiveTab to ensure that you get the expected results. When in a event, you should call GetCurrentEventFirer to get the index of the tab that is firing this event. Once you have this index, you will normally set the active tab to be this index and then interact with the control as you would interact with a regular OGX control.
NOTEBecause the GridPrintPreview method is not exposed in the GridTab control, the print preview functionality is not available when using the Tab version of the Grid.
4.2
1. Create a new VB project. 2. Add the Objective Grid Tab Control to the Toolbox. 3. Place a tab control on the form: Gridocxtab. 4. Add two command buttons, as shown in Figure 14: cmdAddTab cmdRemoveTab
58
Dim style As Object Dim range As Object style = Gridocxtab1.CreateStyleObject() range = Gridocxtab1.CreateRangeObject() range.InitRange(1, 1, 1, 1) style.SetValue("New Tab") Gridocxtab1.SetStyleRange(range, style, GRIDTABLib.constantdefs.gxOverride) Gridocxtab1.ActiveTab = iCurrentTab End Sub Private Sub cmdRemoveTab_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles cmdRemoveTab.Click Dim b As Boolean If (Gridocxtab1.NumberOfTabs > 1) Then b = Gridocxtab1.RemoveTab(0) End If End Sub
Private Sub Gridocxtab1_PreInitialUpdate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Gridocxtab1.PreInitialUpdate Gridocxtab1.ActiveTab = 0 Gridocxtab1.EnableAsDropTarget() Gridocxtab1.EnableGridAsOleSource(0) Dim retval As Object ' This is an example for the tab control. There are only ' two differences between the tab control ' and the regular control. ' 1) ' The tab control's methods and properties are always ' delegated to what is called the Active Tab ' This tab need not be the tab that the users is ' interacting with and can be changed with a ' call to ActiveTab. Always call ActiveTab to ensure ' that you get the expected results ' 2) ' When in a event call GetCurrentEventFirer to get the ' index of the tab that is firing this event ' Once you have this index you will normally set the ' active tab to be this index and then ' interact with the control as you would interact with a ' regular OGX ActiveX control ' The sample code shown below illustrates this simple ' mechanism for ineracting with the tabs ' special note: use this event only for initializing ' original tabs ' Initialize inserted tabs after inserting them retval = Gridocxtab1.GetCurrentEventFirer() Dim iCurrentTab As Short If (retval = 0) Then iCurrentTab = Gridocxtab1.ActiveTab Gridocxtab1.ActiveTab = 0 Gridocxtab1.NumRows = 100 Gridocxtab1.NumCols = 10 Gridocxtab1.ActiveTab = iCurrentTab End If
If retval = 1 Then
60
g_bInitTab = False iCurrentTab = Gridocxtab1.ActiveTab Gridocxtab1.ActiveTab = 1 Gridocxtab1.NumRows = 10 Gridocxtab1.NumCols = 100 Gridocxtab1.ActiveTab = iCurrentTab End If End Sub
4.3
Important Notes
4.3.1 Sorting Columns
In most cases, using grid tabs is the same as using regular grids (except that you have to set the ActiveTab first). However, the SortCols method that is used to sort columns in regular grids does not exist for GridTabs. As a result, you must use a different technique to enable sorting. You have several options: You can use multiple instances of the 'nodb' base control, installed on the panels of a tab control. The 'nodb' base control provides full sorting support. You can implement a sorting function in VB, which programmatically reorder the rows of the grid with the needed criteria. You can use HorizontalSorting and VerticalSorting to give the user the abiliy to sort the grid by double-clicking. Fortunately both of these methods are exposed in the TabGrid control. The 'HorizontalSorting' and the 'VerticalSorting' properties of the Gridocx object do not have any visual effect on the grid, but they enable/disable the feature of sorting the grid when the user double-clicks on a row/column header. On the double-click, the grid gets sorted based on the selected row/ column. Put the following code in the PreInitialUpdate event handler for the GridocxTab control to enable sorting:
Private Sub Gridocxtab1_PreInitialUpdate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Gridocxtab1.PreInitialUpdate Dim retval As Integer retval = Gridocx1.GetCurrentEventFirer() Chapter 4 Tab Control Tutorial 61
Dim iCurrentTab As Integer If (retval = 0) Then iCurrentTab = Gridocx1.ActiveTab Gridocx1.ActiveTab = 0 Gridocx1.NumRows = 10 Gridocx1.NumCols = 10 Gridocx1.HorizontalSorting = True Gridocx1.ActiveTab = iCurrentTab End If If (retval = 1) Then iCurrentTab = Gridocx1.ActiveTab Gridocx1.ActiveTab = 1 Gridocx1.NumRows = 10 Gridocx1.NumCols = 10 Gridocx1.HorizontalSorting = True Gridocx1.ActiveTab = iCurrentTab End If
When you double-click on a column header, the grid gets sorted based on the values contained in the cells of these columns.
62
Database Tutorial
THIS CHAPTER INCLUDES: Register a Database
Objective Grid DAO control Important Terms ODBC control Objective Grid ADO control
CHAPTER 5
5.1
Register a Database
In order to access a database as a data source you must first register it as a datasource. Use the ODBC Data Source Administrator to register the \Rogue Wave\Objective Grid for ActiveX\samples\Northwind.mdb database. Youll need to register this data source before continuing with the ODBC tutorial.
NOTEThis section gives you step by step instructions. If you dont need the details, skip this section.
1. Locate the Northwind.mdb file in the \Rogue Wave\Objective Grid for ActiveX\samples\ folder. Make sure your copy of Northwind.mdb is not read-only. 2. If you are using Windows 2000 or Windows XP, start the ODBC Data Source Administrator by double-clicking the Data Sources (ODBC) icon in the Control Panel\Administrative Tools.
NOTEIf your machine is running a different operating system, you may need to search around a bit to locate the icon for the ODBC Data Source Administrator.
3. Click the tab corresponding to the category of data source you want to register. For example, an ODBC File data source allows you to connect to a data provider. File DSNs can be shared by users who have the same drivers installed. An ODBC System data source is visible to all users on the machine, including NT services. Figure 15, Figure 16, and Figure 17 explain the difference between the three different categories of ODBC data sources.
NOTEYou must use a System DSN instead of a FileDSN if you plan to have your application open the data source automatically with a connect string.
5.1.1
1. After starting the ODBC Data Source Administrator, select the File DSN tab.
64
3. Select the driver that corresponds to Access (*.mdb). 4. Click Next. 5. In the Create New Data Source dialog, type MyODBCDataSource as the Data Source Name.
66
6. Click Next.
7. Click Finish.
68
8. Click Select.
9. Navigate to the samples folder and select the name of the database (Northwind.mdb). Figure 20 Selecting the Database
70
13. Make sure the MyODBCDataSource database shows up correctly. Figure 21 Database has been correctly registered
5.1.2
72
3. Select the driver that corresponds to Access (*.mdb). 4. Click Finish. 5. In the ODBC Microsoft Access Setup dialog, type Northwind as the Data Source Name. (You dont need to add a description.) 6. Click Select. 7. Navigate to the samples folder and select the name of the database (Northwind.mdb). 8. Click OK. 9. Make sure the Northwind database shows up correctly. 10. Click OK.
5.2
Important Terms
ODBC data sources are typically abstracted in most environments using databases and recordsets. The database typically represents the connection and the recordset represents the actual data. We will now see how we can create and use these objects in the context of Objective Grid. Your Visual Basic application can communicate with a database through an OGX control. The name of the database object in this example is database (declared as a global object). The following code demonstrates how this is done:
'database is a global object Dim database As Object database = GridocxOdbc1.CreateDatabaseObject() database.Open("", False, False, "", False)
A recordset is a virtual table created by an SQL statement. In our example recset is the name of the recordset object declared in the PreInitialUpdate procedure.
Dim recset As Object recset = GridocxOdbc1.CreateRecordsetObject()
The connection between the recordset and database is created with the following code:
recset.setdatabase(database) GridocxOdbc1.SetRecordset(recset)
The Northwind database has a table called Customers. Well be retrieving the data from this table to display in our grid. The GridOpenRecordset method of the Gridocx object requires the following steps before you will be able to call it: 1. Open a registered database using the CreateDatabaseObject method and the Open method of the OGDatabase object. 2. Create a new recordset using CreateRecordsetObject and assign the database to the recordset with the SetDatabase method of the OGRecordset object. 3. Create a query on the recordset using the Query method of the OGRecordset object. 4. Assign the recordset to the grid using the GridOpenRecordset method of the Gridocx object.
74
In the next section well add code to view a data source. Try the following tutorial to see how all of this fits together.
5.3
ODBC control
1. Create a new Visual Basic project. 2. Name the form frmODBCQuery. 3. Select Customize Toolbox from the Tools menu. 4. Select Objective Grid ODBC control. 5. Click OK. 6. Click the GridocxOdbc icon and create an instance of this control on the form. 7. Change the name of the OGX control to GridocxOdbc. 8. To add a menu, double click the MainMenu component in the Toolbox. 9. Using Table 5 as reference, add a menu control as follows:
Table 5 Menu Items Caption New SQL Query Requery Print preview Exit Name NewQuery Requery ppview Exit
76
10. Choose Project|Add Windows Form to create a second form (frmQuery) as follows:
Name the text box txtQuery. Name the buttons cmdOK and cmdCancel. 11. Add the following code for frmQuery:
Private Sub cmdOK_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdOK.Click Me.Hide() End Sub Private Sub cmdCancel_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles cmdCancel.Click Query.Text = "" Me.Hide() End Sub Private Sub frmQuery_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load 'Default query. Shows all fields from the Customers table. txtQuery.Text = "SELECT * FROM Customers" End Sub
database.Open("", False, False, "", False) recset = GridocxOdbc.CreateRecordsetObject() recset.setdatabase(database) Dim q1 As New frmQuery() q1.ShowDialog() If (q1.txtQuery.Text = "") Then q1 = Nothing Exit Sub End If recset.SetSqlQuery(q1.txtQuery.Text) 'specify the connect string to open without the DSN dialog 'recset.SetConnectString("ODBC;DSN=Northwind") GridocxOdbc.SetRecordset(recset) End Sub Public Sub NewQuery_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles NewQuery.Click Dim recset As Object database = GridocxOdbc.CreateDatabaseObject() database.Open("", False, False, "", False) recset = GridocxOdbc.CreateRecordsetObject() recset.setdatabase(database) Dim q1 As New frmQuery() q1.ShowDialog() If (q1.txtQuery.Text = "") Then q1 = Nothing Exit Sub End If recset.SetSqlQuery(q1.txtQuery.Text) GridocxOdbc.SetRecordset(recset) GridocxOdbc.GridOpenRecordset() End Sub Public Sub Requery_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles Requery.Click GridocxOdbc.GridRequery() End Sub Public Sub ppview_Click(ByVal eventSender As System.Object,
78
ByVal eventArgs As System.EventArgs) Handles ppview.Click GridocxOdbc.GridPrintPreview(Me.GridocxOdbc) End Sub Public Sub ExitApp_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles ExitApp.Click End End Sub Private Sub GridocxOdbc_OnGridInitialUpdate(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GridocxOdbc.OnGridInitialUpdate 'SetGridColHeaderText(GridocxOdbc, 1, "column01") 'SetGridColHeaderText(GridocxOdbc, 2, "column02") 'SetGridColHeaderText(GridocxOdbc, 3, "column03") End Sub Public Sub SetGridColHeaderText(ByRef grid As AxGRIDOCXODBCLib.AxGridocxOdbc, ByRef Col As Short, ByRef HeaderText As String) Dim range As Object Dim style As Object range = grid.CreateRangeObject() style = grid.CreateStyleObject() range.InitRange(Col, 0, Col, 0) style.SetValue(HeaderText) grid.SetStyleRange(range, style, GRIDOCXODBCLib.constantdefs.gxOverride) End Sub
NOTENotice the code in the PreInitialUpdate() event. This code is typically placed in the PreInitialUpdate() event for the ActiveX control. While this is not required, it is recommended, since it gives the control the opportunity to initialize itself when it first appears.
The ODBC control also supports changing the query. 14. Change the SQL statement if desired or leave the default to extract all fields and all records. (Customers is the name of the table from which we are retrieving data.) The code involved in changing the query is shown below.
Private Sub NewQuery_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles NewQuery.Click Dim recset As Object database = GridocxOdbc.CreateDatabaseObject() database.Open("", False, False, "", False) recset = GridocxOdbc.CreateRecordsetObject() recset.setdatabase(database) Dim q1 As New frmQuery() q1.ShowDialog() If (q1.txtQuery.Text = "") Then q1 = Nothing Exit Sub End If recset.SetSqlQuery(q1.txtQuery.Text) GridocxOdbc.SetRecordset(recset) GridocxOdbc.GridOpenRecordset() End Sub
80
16. If you do not specify the connect string the ODBC select DSN dialog (Figure 24) will be displayed. Figure 24 Select Data Source
18. Select the data source and click OK. 19. If you run the application you should see the data source in grid form as shown Figure 26. The data source can be edited too, if this is allowed. 20. Test the ODBC Query|New SQL Query feature by modifying the SQL statement to select records from either the Employees table or the Products table.
82
NOTEIf you are displaying static data (snapshots) and wish to have the grid updated to reflect the latest updates, you can use the Requery() method.
21. Uncomment the following line to allow the use of a connect string so that you dont need to select the database each time you run the application:
recset.SetConnectString("ODBC;DSN=Northwind")
5.4
5.5
with the steps involved in using Objective Grid's ADO support to access data across the Internet. First let us identify the three basic participants involved when accessing data in this manner: Database server This is the server machine on which the database resides. The database itself can be anything (Access, SQL server, Oracle etc) that can be setup as an ODBC source. IIS server machine This machine is typically a machine where the IIS Server is running. Client machine This is any machine on the Internet that had IE4.01 (or later) installed. (Users of earlier IE versions will have to install the RDS run time.) The user does not have to have any other components installed.
5.5.1
Steps
5.5.1.1
On the IISServer
1. Install RDS, if not already installed, on the IIS Server machine. Please check www.microsoft.com/data for more details and downloading. Please follow the instructions in the documentation that ships with RDS and make sure that RDS is properly installed on your machine. Register adodll.dll using regsvr32.exe. The files required for building this VB ActiveX dll are installed under VB6//Samples//adodll. The compiled binary is available under vb6//samples//adodll/bin. After the above have been successfully registered, locate the file safe.reg in your OGX installation (installed under \\samples\\VB6\\adodll). Double-click this file to enter the information that it has in the registry. This registry file marks the adodll.dll file as safe for scripting and also adds an entry in the registry such that the dll can be used to create components from a client machine.
NOTEYou need administrative rights to be able to enter this in the registry.
2. Create a virtual directory under your IIS root directory. You can name this anything you want. You have to set the proper security for this directory. For this choose properties. Set the security that is appropriate for your context.
84 Objective Grid for ActiveX Users Guide
NOTERefer to the RDS documentation for more information on this. A chapter called "Getting a Recordset to the Client" in the RDS documentation is particularly useful for understanding this.
3. Copy the asp file, ogrid.asp from the samples\\vb6\\adodll\bin directory to the virtual directory that you created in step2. 4. Create an ODBC data conection. Modify the ogrid.asp file so that the correct connect string and SQL string are specified. The file only has a placeholder by default.
5.5.1.2
Use regsvr32.exe to register the OGX Ado grid control. (The grid does not need to be registered on the server. It is only required if you want to do any local testing.) You can alternatively package the control in a cab file for download and installation on an as-needed basis.
5.5.1.3
Testing
Run IE from the client machine and point it to the server (http:\\my server) and to the ogrid.asp file in your virtual directory setup on that machine. The page should display the grid with data from your database.
86
CHAPTER 6
6.1
Introduction
6.1.1 What is the Objective Grid Designer?
The Objective Grid Designer is a tool that lets you design the layout of a grid and save this layout to a file. This layout file can be loaded at run time in order to initialize grids in your application. For example, if you are designing a dialog with a grid where the user should enter data, you can specify the initial cell contents, parameter object settings, row heights and column widths with the grid designer. This layout can be loaded into your grid with one API call. There is no need to specify all the formatting of the grid with code. You can also use a mixed approach, in which you load the layout and still apply certain formatting with code (for example, if you want to load a large choice list from another text file).
6.1.2
When the Objective Grid Designer is used in stand-alone mode, the designer is started as a separate executable (C:\Program Files\Rogue Wave\Objective Grid for ActiveX\Bin\ogdesign.exe) and is run stand-alone. The output of the designer is saved to a binary .ogl file. With this in mind, let us proceed to look at how we can use the designer.
6.2
1. Start the Grid Designer in stand-alone mode by launching \Rogue Wave\Objective Grid for ActiveX\Bin\ogdesign.exe.
2. Choose Layout. 3. Click OK. 4. The Objective Grid Designer window appears.
88
5. To customize the window layout, double-click on any Title Bar border to undock that window. You can move and/or resize undocked windows.
6.3
6.3.1
Grid Interface
The main component (shown in Figure 29) is the grid interface. This is the as will be model. (It reflects the grid that will be created when you use the layout file in your application.)
90
6.3.2
General Settings
Figure 30 shows the property page for General Settings. General settings are settings that can be applied to the whole grid.
The General Settings property page has six different tabs, corresponding to the following categories: Data Use the data property page (see Figure 30) to set the row count and column count. For example, you can set the row count to 30 rows and the column count to 10. You can also set the number of frozen rows and frozen columns with this property page. Notice that the grid view updates immediately to reflect the changed grid interface. Some of the other options that you can set on this page include header-specific details and whether or not the row and column header should be numbered. User Actions The user actions page controls many aspects of how the user will interact with the grid. This interface can be very useful for usability testing of your grid interface. You can configure the grid to suit the needs of users. You can specify whether the grid should support rearranging columns, allow resizing rows and columns, and the minimum size of row and columns, among other settings. Try changing the settings and interacting with the grid. Notice that, at the bottom of the list, you can also specify whether the grid should add support for sorting. Usually you will want to turn on sorting.
92
Selection Often it may be difficult to decide on the various selection modes that your grid interface should support. You should take the time to select every option on this page to see how the grid interface interacts with each setting. This will give you a good feel for the selection interface that the grid supports and how this can be best customized to suit your needs. There is also support for some specific Excel emulation features. You should try all of these to see if there is some setting that your application cannot do without! Another important aspect of this page is that it supports setting the grid in list box mode. You can choose between multiple and single selection list boxes. Check out how the grid list box modes interact with the selection modes. There is no end to the amount of prototyping that you can do with the designer!
Sheet Once you are content with the settings for the selection page, move to the Sheet page, which also has several interesting features, including support for features such as merge cells and floating cells. Check out the Undo/ Redo buffer as well. Look at optimizing this value. If you have memory constraints, you should look at the undo/redo stack that is correct for your applications. And what better way to test this than with the Objective Grid designer!
94
Display The display property page can be used to change the color of the grid lines, the tracking lines, dragging lines, and other aspects of the grid. You can also specify whether or not these lines should be displayed.
Printing The printing section specifies all printer-related settings. You can change the settings, choose print preview (from the main grid frame), and get instant feedback on how the layout would appear when printed.
96
6.3.3
Cell Settings
The property page for cell settings is shown in Figure 31. This property page allows you to work on the current cell or a group of cells and allows you to customize any aspect of these.
Most of the attributes on this page are self-explanatory. However, a beginning user might find some of the attributes (such as the role of user attributes and how they fit into the grid framework) to be a bit more obscure. Notice that this page includes support for setting these attributes.
6.4
98
2. You will see the designer and the property pages pop up. 3. Select a couple of cells (A1:A2) and use the cell type attribute on this page to set the type of control (choose the cell type to be a spin edit control).
NOTEYou dont need to select the check box to the left of the setting. Once you choose the correct cell type from the drop-down list, the check box will be selected automatically.
4. Scroll down the Cell Settings window (half way down the list) and you will see two user attributes, SpinBound Max Value and SpinBound Min Value. Set these to 100 and 10, respectively. You will observe that the spin edit control now validates the input in the cell between these two boundaries. Because it knows about these user attributes, it always checks to see if they have any meaningful values and acts accordingly. Similarly, several other controls have user attributes that they know about. These user attributes are presented on the page and can be used to control several aspects of the controls that know them. Remember user attributes pertain only to one control. SpinBound Max Value will validate only a spin control and not say, an edit control. You are now ready to create a fully functional grid program without writing much code. 5. Save this ogl file (myfirstlayout.ogl), noting the path to it. 6. Exit the Designer.
6.5
3. Run the program. 4. Test the spin edit control buttons. Just for fun, try to exceed the 10-100 range. Figure 32 Grid Layout From a Layout File
Important notes on using the designer: To set properties use either the designer or the ActiveX property interface. Using both can be confusing and counter-productive. Objective Grid Designer is a tool for programmers. It is not advanced enough to be given out to end users. The end-user will probably not understand all the settings you can specify with the property sheets.
NOTEThis kind of direct linking with the designer is currently supported only for the base grid control and not for the tab and databound grid controls.
100
Samples
THIS CHAPTER INCLUDES: Introduction
Troubleshooting Internet (CAB) Sample Visual Basic Samples
CHAPTER 7
7.1
Introduction
NOTESeeSection 4.6.1, Sample Distribution and Reorganization, in the Getting Started User's Guide.
comes with several samples that you can use to learn about the functionality of the product. Figure 33 shows the location of several Visual Basic 6 and Visual Basic 7 samples. You can find these samples under the \\Rogue Wave\Objective Grid for ActiveX\samples folder. Figure 33 Samples
NOTEBefore running any of the samples, register the OGX controls with regsvr32.exe.
7.2
Change this to point to the location of ogocx.cab on your machine (\Rogue Wave\Objective Grid for ActiveX\samples\Internet\cab sample).
... <OBJECT ID="Gridocx1" WIDTH=825 HEIGHT=591 CLASSID="CLSID:6937B443-6494-11CF-97D0-524153480000" CODEBASE="C:\Program Files\Rogue Wave\Objective Grid for ActiveX\samples\Internet\cab sample"> <PARAM NAME="_Version" VALUE="131072"> <PARAM NAME="_ExtentX" VALUE="5948"> <PARAM NAME="_ExtentY" VALUE="7694"> <PARAM NAME="_StockProps" VALUE="0"> <PARAM NAME="SmartResize" VALUE="1"> <PARAM NAME="NumCols" VALUE="10"> <PARAM NAME="NumRows" VALUE="10"> </OBJECT> </BODY> </HTML> NOTEIf you are running a Web server you can make this file available on a virtual directory and point to that path.
After making the above change, you should be able to see the Objective Grid Control displayed inside of your browser when you open grid.htm.
NOTE\\Rogue Wave\Objective Grid for ActiveX\sample\Internet\cab sampleVC.NET contains a cab file which has the OGX controls and the supporting dlls built with the VC.NET compiler.
The \samples\Internet\cab sampleVC7 directory contains a cab file which has prebuilt VC.NET OGX components and all supporting dlls.
7.3
102
This sample illustrates ADO-based data access (local and remote). Please refer to Section 5.5, Objective Grid ADO control, for more information on this sample. Formula This is a formula-enabled sample that includes some text on formula support in the grid. This text is stored in a layout file (main.ogf). A list of mathematical functions is stored in math.ogf. You can view both of these layout files in the Objective Grid Designer. The following code shows how formula support can be enabled in a grid.
Private Sub Form1_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load Gridocx1.EnableFormulaSupport() Gridocx1.InitFromLayout(..\main.ogf) End Sub
The OGF file is a formula spreadsheet configuration that you can create using the Objective Grid Designer in stand-alone modality. The InitFromLayout method allows retrieving the OGL/OGF file, specifying the path where the file is installed. If you use the grid with the Formula Engine enabled, you need to create an OGF file using the Grid Designer. You do not need to create an OGF file if you do not need to initialize/re-initialize your formula spreadsheet at run-time. The OGF file is the equivalent of the OGL file and it gets created when you choose the Formula configuration from the Grid Designer used in stand-alone mode. The OGF file contains the initialization style and values for the grid and you can load it at run-time using the InitFromLayout method of the grid object. Gridapp This sample illustrates most of the generic ways in which the control can be used. It also shows how the events generated (for example GetStyleRowCol) can be used as a virtual handler WRT grid. This sample also illustrates the use of different parameter objects for the different views presented. Refer to Chapter 3, Controls,for more information about this sample.
104
TabBase This sample illustrates basic usage of the tab control. The important properties are ActiveTab, tab captions and classnames. The class names will be
parsed to set the number of tabs and also to create the correct class of tab. Tabs can also be inserted and removed at run time.
7.4
Troubleshooting
7.4.1 CAB Sample
If you are unable to see the control because of security:
106
Change the security to Medium or Low (please read associated precautions). This control is not signed. If the control downloads but does not display itself: It is possible that one or more supporting dlls are available on the target machine, but are not in the current system path. Check for the presence of all the dlls listed in the cce.inf file and see if they are in an accessible path.
7.4.1.1
1. You can obtain the cab signing files from Microsoft. (They are also on the Visual Studio CD.) 2. Once you have these files on your machine you can use cce.inf as a template to create your own .inf file. 3. Add any files that may be specific to your application. 4. After you have edited your inf file, open cabnew.bat. This simply runs cabarc with the following command line:
cabarc -s 6144 n ogocx.cab mfc70.dll msvcr70.dll olepro32.dll og90nodbas.dll ogc90r.dll ogocxgrd.ocx cce.inf
5. Change this batch file to include files that are specific to your application. (cce.lpk was generated by LPK_TOOL.exe, which ships with VB.) 6. Change the n option to change the name of the resultant cab file. 7. Save the changed batch file and run it to generate your new cab file.
7.4.1.2
More Information
For more information, please refer to Design, Test, and Deployment of ActiveX Controls and Documents Over the Internet, presented by Ken Spencer and available on MSDN.
7.4.2
General
The samples rely on a registry entry that specifies where the files were installed, to get at the sample layout files, bitmaps, etc. If you moved files around after the installation or if the registry was not properly updated during installation, it is possible that one or more of the files cannot be located when running the samples. The key that should have the path name is:
The keyname is InstallDir and the value is the path of your OGX root install. For example, if you installed the OGX to C:\Program Files\Rogue Wave\Objective Grid for ActiveX, then this key will look like this:
InstallDir ActiveX" "C:\Program Files\Rogue Wave\Objective Grid for
108
Redistribution
THIS CHAPTER INCLUDES: Introduction for ActiveX Applications
Naming Conventions Distributing Objective Grid
CHAPTER 8
8.1
Introduction
Please read the license agreement that was shipped with your Objective Grid for ActiveX package. You are bound by the licensing restrictions contained in that document. Do not use this product unless you can accept all the terms of the license agreement. You can use all the files accompanying this product for development of an application. You can distribute the Objective Grid for ActiveX Dynamic Link Libraries (DLLs) according to the terms of the license agreement. Your applications can also statically link to the grid, in which case you do not need to redistribute any Objective Grid for ActiveX files. There is no restriction where the Grid CCE DLLs need to be installed.The control looks for the DLLs in the same directory where the OCX is located before and then, if it cannot find them, in the system path. The CCE.LIC file must not be redistributed because it would allow a third party user to use the Grid CCE control at design time.
8.2
Naming Conventions
The library naming conventions are illustrated below for the default library names. Figure 34 Library Naming Conventions
8.3
The following files can be shipped with your application when needed: Table 6 Redistributable Files File
OGas*.dll
Platform Win32
MFC Version Depends on build. PostFix is added based on supported version. As above
OGaus*.dll
Win32 Unicode
110
NOTEYou may not redistribute Objective Grid for ActiveX header files, source files, or sample files. You also may not expose the Objective Grid for ActiveX programming interface in your program (e.g. a DLL or OCX/ ActiveX that lets a user create a grid).
System DLLs
olepro32.dll
The og90*.dll names that are given above are taken from the configurations that we ship with OGX. We ship the prebuilt release dlls for these configurations, as well as the prebuilt Unicode Release OGX components that take advantage of XP Visual Styles. The build wizard files for these are also available in case you wish to rebuild these libraries.
8.3.2
Registering a Component
To distribute applications you create with the OGX control, you must install and register it on the user's computer. The Package and Deployment Wizard, an AddIn provided with Visual Basic, provides tools to help you do that. A deployment file (ogocxgrd.dep) is provided in the Redistribution folder of your installation for use with the VB6 Package and Deployment Wizard.
NOTEPlease refer to your Visual Basic manual for details on using the Package and Deployment Wizard.
NOTEMake sure that the version of OGC90r.dll that you ship links to the same version of the Objecive Grid DLL that your control links to.
Table 9 Version numbers for ogc90r.dll Support ADO support DAO support ODBC support No DB support Version 9.0.0.0 9.0.0.0 9.0.0.0 9.0.0.0
If you are using the pre-built version, follow the same pattern as with the OG ActiveX version and install these dlls into the same folder as the controls that use them. The Objective Grid ActiveX control can also be used with Microsoft Internet Explorer. You can create a cab file for automatic download and installation.
8.3.3
Licensing
All of the pre-built controls are licensed and require that the cce.lic file be installed into the same directory as the controls on any system that is to be used in design mode.
NOTEPlease do not distribute the cce.lic file to end users.
112
Objects
THIS CHAPTER INCLUDES: Architecture Introduction
Parameter object Style Object Range Object Range List Brush Object Data Object Font Object Stylesmap Print info Object Properties Object State Object
CHAPTER 9
9.1
Architecture Introduction
Objective Grid for ActiveX has an object-oriented architecture. When working with Objective Grid for ActiveX for the first time, you might find useful this brief overview of some of these objects and their place in the Objective Grid for ActiveX world. Three essential basic automation objects form the core of the Objective Grid for ActiveX architecture: The Style object (OGStyle) The Range object (OGRange) The Parameter object (OGGridParam, OGAdoParam, OGDaoParam, OGDbParam)
NOTEOGGridParam is the parameter object for either the base control or the tab control.
All of these objects (as well as several others) are discussed in detail in this chapter. Figure 35 lists all of the OGX Automation objects, while Table 10 lists the OGX objects, their class names, and the methods used to create them.
114
Table 10 Objects Object Equivalent OG/MFC Class CGXGridParam CGXDbParam Note Method to Create
OGGridParam
for base grid or tab control ODBC databound control specific ADO databound control specific DAO databound control specific
CreateParamObject
OGDbParam
CreateDbParamObject
OGAdoParam
CGXAdoParam
CreateAdoParamObject
OGDaoParam
CGXDaoParam
CreateDaoParamObject
Table 10 Objects (Continued) Object Equivalent OG/MFC Class Note Method to Create
OGDaoDatabase
DAO databound control specific ODBC databound control specific ODBC databound control specific DAO databound control specific CGXData CGXFont
CreateDaoDatabaseObject
OGDatabase
CreateDatabaseObject
OGRecordset
CreateRecordsetObject
OGDaoRecordset
CreateDaoRecordsetObject
OGData OGFont OGHint OGPen OGPoint OGPrintInfo OGPINFO OGProperties OGRect OGSize OGSortInfo
CGXPen
CGXProperties
116
9.2
Parameter object
The parameter object (OGGridParam, OGAdoParam, OGDaoParam, OGDbParam) is the data object for the whole grid. Internally this object stores all the style objects that the grid control has. We do not normally have to interact with this object directly. However, there are certain instances where interacting with this object may be useful. For example, if you wish to quickly initialize the grid control with different data, it is possible to create and initialize different parameter objects. These objects can then be used to initialize the grid. Please refer to the gridapp sample for an example of this approach. The parameter object is similar to the document in the MFC document/view paradigm. The parameter object contains or points to all necessary data for persisting the state of the grid object. Parameter objects can be stored in documents and serialized or can be used as members of other classes. Parameter objects can also be shared between grid objects. The OGGridParam object maintains attributes and objects being shared by several grid views of the same data. It has references to the styles map with the grid-wide base styles, the properties object, the data object with the cell contents, the undo/ redo list, selected ranges, and covered cells. By default, the parameter object - an instance of OGGridParam - is created in the grid's OnGridInitialUpdate method. Each grid must have a valid parameter object, and it owns an associated flag which indicates whether the parameter object should be deleted when the grid is closed. The parameter object can be maintained either by the application or by the grid itself. That means you can declare an object in the application (typically a global object) and simply pass the object to the grid (using SetParam).
9.3
Style Object
Cells in a grid are defined by internally stored style objects. The style object (OGStyle) is a data object that completely describes a cell in Objective Grid. The style object forms the core for formatting cells in the grid and can be seen as an object that completely defines a cell. This means that the style object has all the information (such as the font, font size, interior color, value displayed, and control type) necessary for the grid object to draw the cell and manage its interaction with users. Changing the style object for a cell is equivalent to changing the cell. Style objects for each cell are stored internally by the grid and can be modified and stored with calls to SetStyleRange().
Style.SetInterior() returns a reference to the style object, making it possible for us to call Style.SetInterior().SetValue() all in one stroke.
NOTEIf you want to remove only the changed background color for a cell style put in place with a SetInterior() call, you can simply call SetIncludeInterior(FALSE).
9.3.1
9.3.2
Objective Grid for ActiveX controls have a different model than most ActiveX controls. Most ActiveX controls have a set of methods and properties. Using these methods and properties the control user sets and gets data and interacts with the control. The controls also have an event set that can be used as hooks to listen in to any incidents of interest to the control. This approach works quite well but becomes cumbersome for large controls. Objective Grid ActiveX controls are Object Oriented and support several helper objects that ease interaction with grid controls. Grid properties are typically set by creating and initializing objects. For example, to set the font for a column with Objective Grid for ActiveX we would create a font object and then initialize its various properties. Once these are initialized, we apply this to the grid. Since these objects maintain state, we can easily change one setting and apply the same font object (with one setting changed) to another row. If we had to do that with a traditional control we would end up having to make all the calls that initialize the font yet again. Here is some pseudo code that drives this home:
Create the font Set 10 font properties (SetFaceName, SetSize etc.,) Apply to the first column Change one property (say FaceName) Apply to the second column
118
You can see that the calls add up very quickly, making the use of a traditional control far more complicated than the use of the OGX control. Besides, since the objects are grouped logically (for example, the font object deals with font properties), mastering the API of the Grid ActiveX controls is far easier than mastering a typical monolithic control that has a few hundred methods and properties. Style objects have a method set that can be used to set each and every aspect of the cell. For example, call the SetValue method to set the value of a cell.
Style.SetValue(This is a test)
The style object is also capable of initializing itself from other objects, each with a specific function. For example, the font object defines all font-specific attributes. Some special methods take other objects, for example a font object:
Style.SetFont(font)
where font is a font object that was previously created and initialized. Style and Range objects can be used to change the appearance of cells and the grid control.
SetStyleRange(range,style,GRIDOCXLib.constantdefs.gxCopy)
where range and style are previously created objects and gxCopy is a constant that will be explained in Section 9.3.5.
9.3.3
Style Inheritance
Objective Grid cells are described completely by style objects. These style objects are normally stored on a cell by cell basis. But Objective Grid also has styles that belong to the whole grid or to rows and columns. Objective Grid for ActiveX divides the styles into several categories: A system-wide standard style. A set of user-defined base styles (Row/Col and Table-wide styles). Cell-specific styles. When the grid draws a cell, it gets the style data in a certain order. 1. The cell-specific style is obtained. The cell-specific style settings override all other style settings. 2. The row and column styles are used to fill any attributes left empty by the cell-specific style.
3. The table-wide style is used to fill any attributes left empty by the row or column styles. 4. The standard grid style is used to continue filling holes. 5. If any base styles are available, they are applied. It is important to note that base style settings will always override the grid-wide styles. The more specific a style is, the more overriding power it has. Suppose the grid system interior color is white and a base style specifies an interior color of red. The base style color will override the standard style. In short, this architecture allows the grid to have a default initialization hierarchy that is very flexible. This is the basis of the grid virtual mode of operation. The process of gathering these styles is loaded with virtuals that can be overridden to handle the style initialization in a dynamic manner. This is what drives the virtual mode. The style inheritance hierarchy is shown in Figure 36. Figure 36 OG ActiveX style inheritance hierarchy
The Standard Style is the object that defines the default appearance of the grid. For example, if the Standard Style interior color is set to Red then all cells that do not have either base, row, column, or specific styles set to have an interior color value will use this value. This architecture provides us with several advantages: We can have a default look and feel without wasting a whole lot of memory to store duplicate information. We can effect a grid-wide change with a single line of code.
120
We can hook in and change the manner in which the style is composed. For example let us say that the grid is drawing cell 2,2. It will first use the Standard Style to compose the basic attributes, then apply any base styles.
9.3.4
User attributes
User attributes are pieces of data that can be stored by the grid on a cell wise basis. These are identified by a double value and can be retrieved on demand.
9.3.5
Style Constants/Modes
One style object can be applied on another style object in several ways. A brief explanation of each of these modes is given below. Table 11 Style Modes Constant gxApplyNew Description Applying only new attributes Only those attributes that are included in the source style and not yet included in the destination style are copied from the source style to the destination style. gxCopy Copying the style All attributes are copied from the source style to the destination style. Attributes that are not included in the source style will be removed from the destination style. gxOverride Overriding attributes Attributes that are included in the source style are copied from the source style to the destination style, regardless of whether or not they are included in the destination style. gxExclude Resetting attributes Attributes that are included in the source style will be removed from the destination style. After the base styles are applied with gxOverride, row and column styles are applied. Lastly the specific style is applied. The specific style is the style that gets stored when we make explicit calls to SetStyleRange(), SetValueRange(), etc. This makes it possible to store all the formatting in column styles (since database columns typically contain the same type of defined data). After this we can hook in at display time and fill in with the required values. This is what data-bound grids do. They initialize the column styles from the database schema and after that read
Chapter 9 Objects 121
the record source whenever the value is to be displayed. We thus avoid having to store a specific value in each cell. Refer to the online reference on GetStyleRowCol() and OnLoadCellStyle() for more information on this aspect of the style architecture. Supplying values in this mode is called Virtual Mode. Regular unbound grids also have row, column, and base styles. However, they have no built-in mechanism to read an external data source. We can choose to change the values of cells in an unbound grid with calls to SetStyleRange() or choose to operate the grid in virtual mode (as is the default with bound grids).
9.3.6
Virtual mode
All of the data-bound controls operate in virtual mode. This means that they access data as needed on a cell by cell basis. This approach has tremendous advantages when it comes to memory consumption as well as the loading and displaying of huge result sets. It also requires us to remember that no data is stored in the grid on a cell by cell basis. The format for a field is stored on a column by column basis. Basically there are two ways to populate the grid: Store the value in the grid itself and allow it to display the value when required In this more commonly used method, the grid stores a copy of the data and uses it to display the value when required. This is what happens when we use SetStyleRange or SetValueRange. The data that is given to the grid is internally stored by the grid (in the parameter and data objects) and is used to display the values when required. Formula grids require this method of storage. Supply data on demand This mode of operation is called Virtual Mode. The grid does not store data. (There are no calls to SetValueRange and SetStyleRange.) Instead we handle (override in C++ terms) the GetStyleRowCol event and supply data there. This data can be accessed from within an external data source or can be generated on demand. A brief example of using the grid in virtual mode is shown below. Changes made when the grid is in virtual mode are typically written back to the data source by handling the StoreStyleRowCol event. This event will be called when a changed cell's value needs to be stored.
Private Sub Gridocx1_GetStyleRowCol(ByVal eventSender As System.Object, ByVal eventArgs As AxGRIDOCXLib._DGridocxEvents_GetStyleRowColEvent) Handles Gridocx1.GetStyleRowCol
122
If eventArgs.nType <> 0 Then Exit Sub End If If eventArgs.nRow = 0 Or eventArgs.nCol = 0 Then Exit Sub End If ' set the value from the generated values stored in the ' global data array (g_data) eventArgs.style.Setvalue(g_data(eventArgs.nRow, eventArgs.nCol)) ' do the simple check to see under which range these values ' fall and set the base style accordingly If g_data(eventArgs.nRow, eventArgs.nCol) < 75 Then eventArgs.style.SetBaseStyle(g_nStyleLow) ElseIf g_data(eventArgs.nRow, eventArgs.nCol) > 75 And g_data(eventArgs.nRow, eventArgs.nCol) < 150 Then eventArgs.style.SetBaseStyle(g_nStyleNormal) Else eventArgs.style.SetBaseStyle(g_nStyleHigh) End If ' Remember it is very easy to change each of these base ' styles too! ' Take a look at the styles sample for details on using ' ChangeBaseStyle ' tell the grid we have modified the style eventArgs.bModified.Value = GRIDOCXLib.ogBoolean.ogTrue End Sub
9.3.7
Style Implementation
Styles are the key to the display of the grid and also define most of its behavior. The style object contains all the information necessary for formatting a cell. A style consists of several attributes, such as text color, borders, control type, and font. A very important feature of the style object is support for combining style objects. For example, you can copy only those attributes from one style to a second style that are not initialized in the second style. Objective Grid for ActiveX uses this feature to enable a kind of inheritance. By specifying a base style, you can tell
Objective Grid for ActiveX that it should inherit attributes from a base style at run time. Attributes in the CGXStyle class are combined with an include bit. This include bit is TRUE when an attribute is initialized. If it is FALSE, the attribute is not initialized. When drawing the grid, Objective Grid for ActiveX fills up all uninitialized attributes of the cell style object with values inherited from the base styles. CGXStyle also supports user-defined style attributes. You can also extend the CGXStyle class with additional attributes. Each style object maintains a map of user attributes and provides a method to change their values. Style objects are created for those cells that should be drawn and initialized at run time in the CGXGridCore member function ComposeStyleRowCol(). ComposeStyleRowCol() takes a row, a column, and a pointer to a style object as arguments. ComposeStyleRowCol() first calls the GetStyleRowCol() function to get any cell specific style attributes. ComposeStyleRowCol() adds to the style object any inherited attributes from the row, the column, and the entire grid. Finally, the styles map is checked for any base styles.
9.3.8
Virtual Functions
Virtual functions allow users of an Object to hook in and change certain behavior of these objects. The Object implementor decides which of these functions are changeable or hookable in this manner. For example, let us say that we have an object that draws an icon on the screen. Let us assume that it holds the handle to the icon that it is supposed to render and renders it when required. Now let us say that we want to be able to add a small logo at the bottom while retaining the rest of the cool functionality that this object provides. If the object implementor had made the drawing function virtual we could then hook in and simply supply our own implementation that would call the default version to do the basic drawing and then do our custom drawing. With C++ this is supported as part of the language and adding this feature to a base class becomes very easy. The implementor will only have to specify the virtual keyword. With ActiveX controls this is normally not available. With OGX, however, very similar functionality is available. We make use of the ActiveX event mechanism (connection points) to implement this. For example in the scenario above we would have a Draw event that would be called by the object each time it is drawn. If there is no code in the Draw handler (in the container) drawing would take place as desired (by the base object code). If we desire to change this drawing entirely or add to it, we can do so. To avoid calling the base class, we can set a special boolean object parameter to ogTrue (to call) or ogFalse (to not call) the base class code. With Objective Grid MFC there are many virtual functions that let users hook in and customize all parts of the grid. OGX provides much of that functionality in the ActiveX space using the event mechanism.
124 Objective Grid for ActiveX Users Guide
9.4
Range Object
The range object (OGRange) defines a rectangular range of cells in the grid. It can be set to represent a single cell, an entire column, an entire row, or the entire table. Explicit coordinates can also be initialized with the top, bottom, left, and right members of the range object. Use one of the following methods to specify the range of cells. Use SetRows() to specify a range of rows. Use SetCols() to specify a range of columns. Use SetTable() to specify the full table. Use SetCells() to specify individual cells. Use InitRange() to set the range to a specific cell. One common point of confusion is the difference between the SetCells() call and the SetRows(), SetCols(), and SetTable() calls. SetCells() defines a rectangular region with well defined top-left and bottom-right cells. This is very similar to defining a rectangular region on the screen using CRect.
SetRows(), SetCols(), and SetTable() define regions without a definite top-left or bottom-right cell. Instead they define the range as being entire rows, entire columns, or the entire table, independent of the actual grid dimensions. This means that as the dimensions of the grid grow or shrink, the realized dimensions of the range will grow or shrink. For example, let's say there is a grid with 10 rows and 10 columns, and a range has been defined using SetCols(2,4). The realized coordinates of this range are top = 0, bottom = 10, left = 2, and right = 4.
Another common misconception is that you can combine calls to SetRows() and SetCols() to define a cell or range of cells. This is not true. Calling SetRows() defines the range as the specified rows. Following up with a call to SetCols() redefines the range as the specified columns. It does not take the union of the two ranges. To define a cell or range of cells, you must explicitly use SetCells().
9.4.1
Creating ranges
Range objects are created with a call to CreateRangeObject() on the control. If the name of the control is gridocx1, the necessary code would look like this:
Dim range As Object range = gridocx1.CreateRangeObject()
126
9.4.2
Initializing ranges
Range objects can be initialized in several ways. Some of the most common ways are listed below: With a call to InitRange:
Range.InitRange(1,1,2,2)
By calling SetCols:
Range.SetCols(1) SetCols will assign the complete column area to this range object.
By calling SetRows:
Range.SetRows(1) SetRows will assign the complete row area to this range object.
9.4.3
Private Sub Gridocx1_OnGridInitialUpdate(ByVal sender As Object, ByVal e As System.EventArgs) Handles Gridocx1.OnGridInitialUpdate Gridocx1.NumCols = 2 Gridocx1.NumRows = 5 End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click Chapter 9 Objects 127
Dim RangeListObj As Object RangeListObj = Gridocx1.CreateRangeListObject Gridocx1.CopyRangeList(RangeListObj, True) MsgBox("GetCount = " & RangeListObj.GetCount) If (RangeListObj.GetCount >= 1) Then Dim Range As Object For Each Range In RangeListObj MsgBox("Range = " & Range.Top & "-" & Range.Left & "-" & Range.Bottom & "- " & Range.Right) Next End If End Sub Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click Dim nRow As Object Dim nCol As Object nRow = Gridocx1.CreateDoubleStateObject nCol = Gridocx1.CreateDoubleStateObject Gridocx1.GetCurrentCell(nRow, nCol) MsgBox(nRow.Value & " " & nCol.Value) End Sub
128
9.5
Range List
COGRangeList maintains a list of range objects. For example, ranges of cells selected by the user are stored in a COGRangeList. You can iterate through the range list as shown below:
Dim rangelist As Object rangelist = Gridocx1.CreateRangeListObject() Gridocx1.CopyRangeList(rangelist, True) If (rangelist.GetCount >= 1) Then Dim range As Object For Each range In rangelist ' Iterate through each element. If (range.IsCells = True) Then Gridocx1.SetCoveredCellsRowCol(range.Top, range.Left, range.bottom, range.Right) End If Next End If
9.6
Brush Object
The OGBrush class is a wrapper for the Windows LOGBRUSH structure. OGBrush also adds support for 33 predefined patterns you can use in your grid. The following attributes are provided by the OGBrush class: Style - Specifies the brush type. Hatch - Specifies the hatch type. Pattern - Specifies the pattern type. Objective Grid offers 33 predefined patterns. Pattern Color - This is the foreground color of the pattern. If the brush is solid, the cell will be filled with this color. Background Color - This is the background of the pattern. If the pattern is empty, the cell will be filled with this color. Normally, a OGBrush object is used to specify the interior brush of a cell.
9.7
Data Object
The COGData class maintains the cell contents of the grid and the row and column base styles. The grid component creates an instance of the data object in OnGridInitialUpdate and attaches it to the parameter object. COGData provides a public interface for setting the number of rows and columns, storing and reading cells, and removing, inserting and moving rows and columns. The names for these methods are the same as in the control. All of the data objects are called from the control when you change the grid by calling MoveRows or InsertRows, for example. If you want to initialize data outside the grid you should call the OGData methods directly.
9.8
Font Object
The COGFont class encapsulates a LOGFont structure. Font objects can be composed. Each attribute of LOGFont is associated with an include bit. When an attribute is initialized, its include bit is TRUE. If the include bit is FALSE, an attribute is invalid. When drawing the grid, Objective Grid fills up all uninitialized attributes of a cell's style object with the base style's attributes. If the GetInclude... methods return FALSE, Objective Grid will try to initialize this attribute with the base style's setting. If the GetInclude method returns TRUE, the specific setting will be used for drawing. The following attributes have an include bit in COGFont class: Font Weight (Bold, Normal) Italic Style Underline Style StrikeOut Style Font Size Face Name Each attribute is associated with four attribute methods. An example is the font size:
'returns the include-bit BOOL GetIncludeSize() const 'changes the include-bit. COGFont SetIncludeSize(BOOL bInclude)
130
'returns the font size. LONG GetSize() const 'changes the font size. COGFont SetSize(LONG size)
NOTEWhen you change the font for the standard style in the grid, the row heights and column widths will grow or shrink proportionally with the font size. If you need to know the new row heights and column widths immediately after changing the font size, you should call UpdateFontMetrics.
9.9
Stylesmap
The COGStylesMap class maintains grid-wide styles that make it possible to group specific kinds of cells and make them have similar attributes. The predefined base styles are: row-header-style, column-header-style and standard-style. Row header cells inherit their attributes from row-header-style. Column headers inherit their attributes from column-header-style. Standard-style is the base style for all cells in the grid. COGStylesMap also maintains controls and user-attributes to be used in the grid. COGStylesMap data can be written to the application profile and loaded from it. Each base style has a unique id. This name is the key for the style-object and can be used in COGStyle::SetBaseStyle to specify the base style. The following attributes specify the ids for the default styles: wStyleStandard specifies the Standard-style.
Chapter 9 Objects 131
wStyleRowHeader specifies the Row Header-style. wStyleColHeader specifies the Column Header-style. These styles are called system styles, which means that the user cannot delete them. The programmer can add more system styles that cannot be deleted by the user. Non-system styles can be added and removed at run time through the stylesdialog.
9.10
9.11
Properties Object
The COGProperties class maintains display and print properties of a grid, such as margins, header/footer, and colors of grid lines. Objective Grid offers some predefined dialog classes which allow the end user to modify these settings at run time. When a grid is initialized, it checks (in OnGridInitialUpdate) if there is an existing property object. If this is not the case, a property-object will be created, and default data will be loaded from the profile. The property-object is maintained by the parameter-object. The COGParam object owns a reference to a property-object and provides accessor methods (GetProperties / SetProperties).
9.12
State Object
State objects (OGBoolState, OGBStrState, OGDoubleState) are simple objects that maintain the state of a particular type of object. For example OGBoolState objects hold a single Boolean property. They are used instead of simple values wherever parameters are passed by reference (i.e. where they are declared and passed in and are expected to be modified inside the method or event). This is required to be compatible with scripts such as VB Script and J Script that do not correctly handle by ref parameters.
132
134
CHAPTER 10
Advanced Topics
THIS CHAPTER INCLUDES: Undo/Redo Architecture Adding Border to Cells Number of Currently Visible Rows Determining selected rows/cells Clear the cells of the grid without selecting the grid
10.1
Undo/Redo Architecture
Objective Grid supports multiple level Undo/Redo. The grid also supports transactions. You can use GridBeginTrans to begin a transaction and then use GridCommitTrans to commit it. You can also roll back with a call to GridRollBack.
Gridocx1.GridBeginTrans("Trans1") 'call grid commands ' SetValueRange ' SetStyleRange ' any other grid command if Check if we need to rollback then Gridocx1.GridRollBack() Else Gridocx1.GridCommitTrans() End if
10.2
style = Gridocx1.CreateStyleObject Dim range As Object range = Gridocx1.CreateRangeObject style.SetBorders(GRIDOCXLib.constantdefs.gxBorderTop, Pen) range.InitRange(2, 2, 4, 2) Gridocx1.SetStyleRange(range, style, GRIDOCXLib.constantdefs.gxOverride) style.Free() style.SetBorders(GRIDOCXLib.constantdefs.gxBorderBottom, Pen) range.InitRange(2, 7, 4, 7) Gridocx1.SetStyleRange(range, style, GRIDOCXLib.constantdefs.gxOverride) style.Free() style.SetBorders(GRIDOCXLib.constantdefs.gxBorderLeft, Pen) range.InitRange(2, 2, 2, 7) Gridocx1.SetStyleRange(range, style, GRIDOCXLib.constantdefs.gxOverride) style.Free() style.SetBorders(GRIDOCXLib.constantdefs.gxBorderRight, Pen) range.InitRange(4, 2, 4, 7) Gridocx1.SetStyleRange(range, style, GRIDOCXLib.constantdefs.gxOverride)
10.3
10.4
136
If you need to retrieve the current cell, you may want to use the 'GetCurrentCell' method of the Gridocx object. The following code shows how to use this method:
Dim nRow As Object Dim nCol As Object nRow = Gridocx1.CreateDoubleStateObject nCol = Gridocx1.CreateDoubleStateObject Gridocx1.GetCurrentCell(nRow, nCol) MsgBox(nRow.Value & " " & nCol.Value)
In order to retrieve a selected range or a multiple selection, you may want to use the following code:
Dim RangeListObj As Object RangeListObj = Gridocx1.CreateRangeListObject Gridocx1.CopyRangeList(RangeListObj, True) MsgBox("GetCount = " & RangeListObj.GetCount) If (RangeListObj.GetCount >= 1) Then Dim Range As Object For Each Range In RangeListObj MsgBox("Range = " & Range.Top & "-" & Range.Left & "-" & Range.Bottom & "-" & Range.Right) Next End If
10.5
Gridocx1.ClearCells(objRangeList, True)
138
PART II
140
CHAPTER 11
Class Summary
Understanding the OGX Source Code Control
11.1
Introduction
NOTEThis chapter will be of interest primarily to users who wish to customize the OG ActiveX controls. If you are planning to use the controls without modifying them, you may skip this chapter.
Before we look at the Objective Grid for ActiveX class summary, this brief description should help you better understand the source.
11.2
Containers can take advantage of the several interfaces (see Chapter 17) typically implemented by ActiveX controls. The idea behind this scheme is to ensure a component-based system that would allow interaction between binary objects that have no special knowledge of each other (other than through the published interfaces). The latest ActiveX specifications only require that ActiveX controls implement the IUnknown interface. (All other interfaces are optional and are derived from IUnknown.) However, most controls implement a plethora of interfaces to provide some functionality. A control that implements only IUnknown would not be very useful. Thus control writers have to code certain generic published interfaces besides their implementation logic. This can often lead to tens of thousands of lines of boiler plate code. Class libraries like MFC have filled in, allowing developers to concenChapter 11 Class Summary 141
trate on their application-specific logic rather than having to worry about generic container handling interfaces. Objective Grid for ActiveX can be seen as a library that fills in this boiler plate code for the Objective Grid class library. To fill in this boiler plate code and to make the Objective Grid libraries fully functional as OLE controls, we had to make some design decisions. To gain a greater understanding of these issues and a better understanding of the product itself, consider some of the original design goals listed below: The ActiveX Grid component should be object-oriented. By being objectoriented, Objective Grid does much to ease the tedium of working with a complex grid control. Over the course of several releases, it has proven to be easily extensible and maintainable in large measure due to the objectoriented nature of the library. Objective Grid for ActiveX should implement the full grid API as OLE automation methods. Objective Grid for ActiveX should provide a mechanism that would, through OLE, enable a functionality that is akin to virtual functions inside C++. Virtual functions are essential to a C++ implementation and we wanted to carry over this tremendous functionality into the ActiveX world (if we could find a good way). There should be automation-based properties that would provide a ready interface to some of the more frequently used grid interface features such as the font, the interior color and such. This is common with most controls and we wanted to be in line with the pack. OGX should provide a variety of data binding options.
Objective Grid does not have a VBX legacy. This gave us the freedom to start work with a component that was written from the ground up with the MFC as its backbone and with object-oriented design as one of its driving factors. To wrap around this component would mean that we would have to retain the object-oriented nature of the Grid to carry its power into the ActiveX environment. To achieve this, we wrapped each of the objects that the grid uses inside C++ as an automation object. These objects are available as floating entities, which can be created by the grid control. We will now proceed to briefly look at how this is accomplished in code. Let us take the example of two objects that are very commonly used by Objective Grid,
142
the style object and the range object. Inside the ActiveX version, both of these very useful objects are available as OLE automation objects. We create them as shown below: Figure 38 Object Creation
When automation calls are made from the controller, the enclosing object simply directs these calls to the embedded instance. We get the functionality of the object in question using this form of containment.
11.2.1.2
Objective Grid for ActiveX implements almost the entire Grid API as automation methods. This part of the source is pretty straight forward and simply delegates to the MFC grid source in many cases. The only part that requires a little explanation is when we use IDispatch pointers. To pass objects to the ActiveX control, we often resort to wrapping these in automation wrappers as explained above and then these pointers are dereferenced to get the actual object that is then used in the call. This helps us to maintain an interface that is almost identical to the Objective Grid C++ interface. For example, SetStyleRange() still looks like this:
SetStyleRange(range object, styleobject, flags)
Let us look at some source that dereferences an automation object IDispatch, uses the resulting object to get at the actual object, and uses it in the source code.
// // // // You can pass either an ODBC or DAO recordset from VBA. This method will assign the contained recordset pointer to the bound grid. This pointer will be used in the call to SetRecordset in the grid's OnInitialUpdate
// To do-- add place holder for vbrecordset wrapper void COGOleCtrl::SetRecordset(LPDISPATCH recordset) {
#ifdef _CCETABGRID DELEGATE_BROWSEMETHOD(SetRecordset(recordset)) #endif #if defined(_CCEDAOGRID) || defined(_CCEODBCGRID) try { // We have to add ref this since VB will release this // object when it wants. We will call release on it later. recordset->AddRef(); COGVBRecordsetPlace* pVBRecset = (COGVBRecordsetPlace*)FromIDispatch(recordset); // // // // if We will store the IDispatch pointer here so that we can call release later from ReleaseObjects. The reason that we have to do this is that we have to prevent VBA from releasing the object at its fancy (m_recordsetWrapper) m_recordsetWrapper->Release();
m_recordsetWrapper = recordset;
COGRecordsetPlace* pRecset = pVBRecset->GetRecordset(); // #if defined(_CCEDAOGRID) pRecset->m_bCheckCacheForDirtyFields = FALSE; #endif //ASSERT(pRecset->IsKindOf(RUNTIME_CLASS(CGXRecordsetPlace))); DELEGATE_BROWSEMETHOD(SetRecordset(pRecset)) if (m_pCtrlRecordset != NULL && m_bOwnRecSet) delete m_pCtrlRecordset; m_pCtrlRecordset = pRecset; m_bOwnRecSet = FALSE; } catch (CMemoryException e) { TRACE(_T("Memory allocation failed in SetRecordset\n")); ThrowError(CTL_E_OUTOFMEMORY, IDS_OG_VBOUTOFMEMORY); } catch(...) { ThrowError(GXCTL_E_INTERNALERROR, IDS_OG_VBINTERNALERROR); } #else return; #endif
144
//unused recordset; }
We get an object that we can then use from the IDispatch pointer and go from there.
11.2.1.3
Anyone who has worked with C++ will agree that one of the most powerful and useful features of the language is the availablity of virtual functions. Objective Grid makes extensive use of this mechanism inside C++ to work the way it does and to provide the ultimate level of customization. We used the ActiveX connection point mechanism to simulate this feature. Any automation controller will provide a way to recognize and implement source interfaces from the control. This interface is not predefined by the controller in any manner and is pretty open to the control implementor.
NOTEFor more information on the connection point mechanism, please refer to the OLE documentation on Connection points and connection point containers. Inside OLE has a very good explanation of this, too.
Essentially when a virtual function is called in the C++ source, we simply fire an event that will be handled by the container. The event will have the exact same signature (conceptually) as the virtual function that generates the event. We also pass any pertinent objects to the controller through this mechanism. The controller (user provided code) will then modify the parameters or handle the function itself and let us know whether we should call the default code (base class) or not through an additional boolean state parameter that is added to the function signature. In the context of Objective Grid, a simple example that would illustrate this mechanism would be GetStyleRowCol. This is a virtual function that gets called just before the grid displays a cell. The style object that will be used to draw this cell is passed to this virtual function. The C++ programmer has the option of changing these parameters based on dynamic data to get a Just in Time display. With an automation controller we would simply fire a GetStyleRowCol event that would be handled by the automation controller provided event code (in the case of Visual Basic, for example, this would be the event-handling code that the VB programmer writes for this event). In the same way that the C++ programmer could modify the style object, the automation controller code could too, giving the automation programmer the same kind of flexibility that the C++ programmer enjoys.
Chapter 11 Class Summary 145
One issue involves optimization. If we were to copy each object that is passed to the virtual override into an automation object, the cost of this would make it prohibitive. Therefore, we simply switch pointers in contained captive automation objects. After each IDispatch call is completed, we switch back the parameters like within a stack.
/////////////////////////////////////////////////////////////////// ////////////// // The OCX wrapper for GetStyleRowCol ///////////////////////////// /////////////// // This event enables virtual binding in VBA ////////////////////// ////////////// /////////////////////////////////////////////////////////////////// ////////////// // When we have to pass objects to VBA during an event call (like // when the style object gets passed everytime GetStyleRowCol gets // called) we implement an effecient interface that sets the // contained pointer in the automation wrapper to the address of // the object being passed. We reset the pointer after the call is // made. Each of the objects (one of) is declared as a member // variable and is preallocated to have maximum performance. // The first few implementations have the complete code and // thereafter we have macros that do the same thing. BOOL COGOleCtrl::GetStyleRowCol(ROWCOL nRow, ROWCOL nCol, CGXStyle& style, GXModifyType mt, int nType) { BOOL bRetval(FALSE); try { bRetval = COGOleGrid::GetStyleRowCol(nRow, nCol, style, mt, nType); } catch(COGSEH_Exception& e) { FireGridError(eventidGetStyleRowCol,e.getSeHNumber(),!e.getExceptio nFlags()); return FALSE; } m_vbStyle->SetStylePtr(&style);
LPDISPATCH lpdispatch = m_vbStyle->GetIDispatch(FALSE); FireGetStyleRowCol(nRow, nCol, &lpdispatch, GetValFrommt(mt), nType, &bRetval); m_vbStyle->ResetStylePtr(); 146 Objective Grid for ActiveX Users Guide
return bRetval; }
NOTEThe code above shows a style member m_vbStyle. The actual OG ActiveX implementation uses a manager object that will return and manage these objects. This is not shown for simplicity.
The OBJECT_DISPATCH and the OBJECTEND_DISPATCH macros wrap most of this functionality. (Please refer to ogolemac.h in \basectl\include for this file.) The remaining part of this chapter provides an overview of the following groups of MFC extension classes: Control Classes Objective Grid for ActiveX provides COleControlderived control classes that form the backbone of the ActiveXs/OCXs. Object Classes Objective Grid ActiveXs are supported by automation objects that mirror the objects available inside the MFC version of Objective Grid. These classes provide the same functionality inside automation controllers. All of these classes are CCmdTarget-derived.
11.3
Control Classes
Figure 39 contains the hierarchy for the Objective Grid ActiveX control classes. Figure 39 Objective Grid ActiveX control class hierarchy
This class hierarchy diagram may be a little different from most you have seen. Thats because Objective Grid for ActiveX, like Objective Grid, uses C++ multiple inheritance (which is not used in MFC). The sections below describe each class in detail and explain why Objective Grid ActiveX uses these classes the way it does.
11.3.1 COGOleControl
COGOleControl derives from COleControl and also from grid-specific classes such as CGXGridCore. The inheritance on the right side is special in that COGOle-
Control will define this side based on the control that is being compiled. Objective Grid ActiveX uses the same code body to generate the controls for basic grid support, ADO support, DAO support, and ODBC support. If you are familiar with the Objective Grid class hierarchy, you will immediately realize that we have to derive from CGXGridCore (for the basic grid) and from CGXAdoGrid (for example) for the ADO control class. This is exactly what the right-side inheritance hierarchy does. Based on preprocesssor switches, this will be defined as CGXGridCore, CGXAdoGrid, etc. The essential fact is that this class on the right side is like a chameleon and acts as a placeholder for the class that best fits the need. The primary implementation of the control class deals with these areas. Implementation of all OLE automation methods, events, and properties. Implementation of the essential drawing code that takes care of both design and run time representation. This class also takes care of rendering the grid to a metafile if required. Implementation of any special code that the control needs to implement features such as ToolTips. Implementation of helper classes that provide a convenient interface between OLE and the grid classes. This kind of inheritance helps us avoid code duplication when dealing with multiple grid classes.
11.4
Object Classes
As mentioned earlier, we support many of the objects that Objective Grid depends on as automation objects that are available inside any automation controller. In MFC terms this means that the object will have to be wrapped in a CCmdTargetderived class. We chose not to rely on multiple inheritance. Instead, we chose containment and delegation. There were a couple of reasons for this: Using multiple inheritance would have meant that we would have had to contend with the possibility of CObject appearing on both sides of the inheritance tree (and all the associated problems). One of the goals of the Objective Grid for ActiveX project was to transfer much of the flexibility that Objective Grid affords inside a C++ environment to the automation controller environment. To this end, we decided to employ events to simulate virtual functions. For example, with
148
the event handling implementation in OGX, it is possible to write code such as this inside an automation controller like Visual Basic.
Private Sub Gridocx1_GetStyleRowCol(ByVal sender As Object, ByVal eventArgs As AxGRIDOCXLib._DGridocxEvents_GetStyleRowColEvent) Handles Gridocx1.GetStyleRowCol 'Simply modify the style object as you see fit! If (eventArgs.nType <> 0) Then Exit Sub End If Dim pen As Object Select Case eventArgs.nCol Case 1 If (eventArgs.nRow <> 0) Then 'The style object can be modified in several ways 'and at the same time. No repeated calls on the 'object eventArgs.style.SetValue("Virtual Col1").SetInterior(RGB(255, 0, 0)) eventArgs.bModified = 1 End If Case 2 If (eventArgs.nRow <> 0) Then 'You can change every aspect of each cell on demand. 'Here we set the color at runtime. Everything is 'object oriented. Each object can be customized in 'several ways, with endless combinations possible. pen = Gridocx1.CreatePenObject() pen.SetColor(RGB(0, 0, 255)) eventArgs.style.SetBorders(4, pen) eventArgs.style.SetValue("Virtual Col2") eventArgs.bModified = 1 End If Case Else eventArgs.bModified = 0 Select End Sub
This is not very different from what you would expect to do inside C++ to achieve the same end. The containment and delegation that we use for the automation objects make possible this kind of functionality. In our automation objects we have an embedded pointer to the real live objects. For example, if we have an automaChapter 11 Class Summary 149
tion style object (COGStyle), we would then have an embedded instance of CGXStyle (the C++ style class that is part of Objective Grid/MFC). All the behavior that is exhibited by the the automation object (COGStyle) is essentially a function of this embedded pointer. This gives us the opportunity to switch the embedded pointer at run time (moving the original pointer to a temporary location) and thereby change the contained object. To get this in perspective, let us look at an example. In the case of CGXGridCore::GetStyleRowCol (a virtual funtion called by the grid to gather style information from the underlying data structures as well as from code just before the grid displays this data), this would simply mean that we switch the pointer in the captive style object. (The control class has captive objects of every type managed by state manager objectsso that it can allocate them when required. It allocates them at startup and then frees them up when done.) The automation object that gets passed to the VB event then has the actual CGXStyle pointer (that we would ordinarily modify inside the C++ virtual GetStyleRowCol). If the VB programmer modifies this style object, then he sets the value of a boolean to TRUE to indicate that he has changed the object passed (this is required since events cannot return values in this case). Back inside C++, we check whether the value has been changed and use the changed value if required. Thus, we accomplish essentially the same kind of change that would be possible inside C++ and offer the same kind of flexibility. Here is the C++ code that accomplishes this, just to give you a feel for our approach:
/////////////////////////////////////////////////////////////////// // The OCX wrapper for GetStyleRowCol ///////////////////////////// ////////////////////////////////////// // This event enables virtual binding in VBA ////////////////////// ///////////////////////////////////////////// // When we have to pass objects to VBA during an event call (like // when the style object gets passed every time GetStyleRowCol gets // called), we implement an efficient interface that sets the // contained pointer in the automation wrapper to the address of // the object being passed. We reset the pointer after the call is // made. Each of the objects (one of each class) is declared as a // member variable and is preallocated to have maximum performance // The first few implementations have the complete code and // thereafter we have macros that do the same thing
150
BOOL COGOleCtrl::GetStyleRowCol(ROWCOL nRow, ROWCOL nCol, CGXStyle& style, GXModifyType mt, int nType) { BOOL bRetval(FALSE); try { bRetval = COGOleGrid::GetStyleRowCol(nRow, nCol, style, mt, nType); } catch(COGSEH_Exception& e) { FireGridError(eventidGetStyleRowCol,e.getSeHNumber(),!e.getExceptio nFlags()); return FALSE; } m_vbStyle->SetStylePtr(&style);
LPDISPATCH lpdispatch = m_vbStyle->GetIDispatch(FALSE); FireGetStyleRowCol(nRow, nCol, &lpdispatch, GetValFrommt(mt), nType, &bRetval); m_vbStyle->ResetStylePtr(); return bRetval; }
As you can see, we just switch pointers at run time. This method has the calls listed. Most methods have macros (e.g. STYLE_DISPATCH) that do the same thing. This kind of interface is very efficient and also enables us to provide the ultimate in flexibility. Figure 40 depicts the layout of the Objective Grid ActiveX object classes. Note how the Automation Object acts as a wrapper around the actual object. Automation calls are made to a visible interface from the automation controller application, such as VB or Access. Because all calls are delegated by proxy, the object class does all the work.
The automation objects that ship with Objective Grid for ActiveX are described in the online help file as well as in the object reference. There are 25 objects, several of which mirror an Objective Grid MFC extension class. Others are helper classes.
152
CHAPTER 12
12.1
Introduction
The Objective Grid for ActiveX control is designed to be used in the Visual Basic environment. It wraps the MFC Objective Grid control in an ActiveX control and exposes an API that enables VB developers to use many of the Objective Grid features. However, some MFC developers want to use the ActiveX version (instead of the MFC version) in their applications. This chapter is for those developers. However, as a general rule, MFC developers will see better performance if they use the MFC version of Objective Grid. You need to install the OG libraries. If you also use the MFC version of Objective Grid, you need to make sure that the correct of version of the libraries are installed.
12.2
Pre-Requisites
Objective Grids ActiveX controls incorporate several ActiveX features that typical ActiveX controls do not use. While this gives our controls unmatched flexibility and extensibility, it also requires that you understand the basics of classes such as COleDispatchDriver. We strongly recommend that you read the MFC documentation on COleDispatchDriver before reading this document or before using the control inside Visual C++.
12.3
2. Choose Visual C++ Projects from Project Types, and select MFC Application from the Templates: frame.. 3. Give your project the name MFC Sample. 4. Select Dialog based as the Application type.
5. Check the Application settings by clicking User Interface Features in the AppWizard. 6. Check the Advanced settings by clicking Advanced Features and check the Class names by selecting Generated Classes in the AppWizard.
154
156
8. Click Finish.
158
10. Select Insert ActiveX Control. 11. Scroll down until you can select Objective Grid Control.
NOTEThere are different controls to choose from, depending on how you want to use the control.
160
14. Build the solution (CTRL+SHIFT+B). 15. Run the program (CTRL+F5). 16. Type something in several cells to try it out. Figure 43 Working MFC App with OGX Control
12.4
Calling Methods
Objective Grid ActiveX methods can be classified as those that require objects as part of the call and those that do not require objects as part of the call.
GetGridRowCount() does not require any objects, while the following call takes a range object and a style object as part of its parameter list: SetStyleRange(LPDISPATCH range, LPDISPATCH style, short mt)
No additional work is required for calling methods that do not require any objects. You can simply call these on the grid OCX as shown below:
// Get a pointer to the OCX CGridocx* pOcx = (CGridocx*)GetDlgItem(IDC_GRIDOCXCTRL1); Chapter 12 Using OGX Control in Visual C++ 161
1. Select Class View from the View menu. 2. Select Add Class from the Project menu after selecting the name of the project in Class View. Figure 44 Starting the ClassWizard
162
3. Select MFC Class From TypeLib from the Templates: frame. Figure 45 Adding a class
4. Select File under Add class from to provide the location of the file containing the type library. Figure 46 Importing from a type library using Class Wizard
5. Point the Add Class From Typelib Wizard to the location of your OGX ActiveX control supporting dll ogc*.* Figure 47 Finding the dll
164
NOTENormally this will be ogc90d.dll or ogc90r.dll unless you are using database support. This file can be found under the bin folder of your OGX installation.
6. Click Save. 7. You will then be prompted with a dialog that shows you all the automation classes that the OGX control servers expose. 8. Select the ones that you want. Select all the classes if you are unsure about what you need and what you dont need.
Figure 49 Automation classes in the extension dlls that the OGX version uses
9. Click Finish and the selected classes will be generated for you. Please remember that the classes that Class Wizard generates are COleDispatchDriver-based and are generated so that you can make the calls to the objects easily. They are always to be mapped to the corresponding COG* classes for referring to the documentation. Let us proceed to look at a few simple calls using the generated objects:
// Declare a style object IOGStyle style; style = pOcx->CreateStyleObject(); // Declare and create a range object IOGRange range; range = pOcx->CreateRangeObject();
166
Please remember that these objects map to COGStyle and COGRange in the documentation.
NOTEIf you use any of the GX_* constants you will have to include gxall.h in your stdafx.h file. Please add _GXNOAUTOLIB to the preprocessor settings to avoid linking with the grid libraries.
Please refer to the documentation on COGStyle for more information on SetControl, SetChoiceList, etc.
style.SetControl(GX_IDS_CTRL_RADIOBTN3D); // 3D radio button control style.SetChoiceList(_T("One\nTwo")); // Set this control for the second column range.SetCols(2);
That is all there is to calling methods on the Objective Grid for ActiveX control.
12.5
12.6
Handling Events
To handle events in an OGX control, please follow the following steps: 1. From Class View, select CMFCSampleDlg. 2. Click Events from the Properties window and select the control ID (IDC_GRIDOCXCTRL1).
168
You will be presented with a list of events that the control supports as shown: Figure 50 Handling events using Class Wizard
3. Select the events that you want (for example, select GetStyleRowCol) and from the drop down list box select <Add> GetStyleRowColGridocxctrl1.
4. The wizard will add the following code for event GetStyleRowCol:
BEGIN_EVENTSINK_MAP(CMFCSampleDlg, CDialog) ON_EVENT(CMFCSampleDlg, IDC_GRIDOCXCTRL1, 1, GetStyleRowColGridocxctrl1, VTS_R8 VTS_R8 VTS_DISPATCH VTS_I2 VTS_I4 VTS_DISPATCH) END_EVENTSINK_MAP() void CMFCSampleDlg::GetStyleRowColGridocxctrl1(double nRow, double nCol, LPDISPATCH style, short mt, long nType, LPDISPATCH bModified){ // TODO: Add your message handler code here }
170
COGStyle pDriver(style); pDriver.SetValue(_T("Test")); COleDispatchDriver dr(bModified); dr.SetProperty(0x1, VT_I2, 1); dr.m_bAutoRelease = FALSE; } }
We create an COGSTYLE object to drive the raw IDispatch pointer that gets passed in to this method. Remember that these events inside the Objective Grid ActiveX behave very much like C++ virtual functions. We can change the value and this will change the value in the grids internal structures as the grid is drawn. (GetStyleRowCol is an event that is generated when the grid is about to be drawn. The style object represents almost all aspects of the cell including its value, etc. These attributes can be changed very easily at run time based on dynamic conditions by overriding GetStyleRowCol as shown above).
12.7
Properties such as these are typically meant for end user manipulation with property sheets and such. As shown above, manipulating them in C++ code is verbose but quite simple.
NOTEIf you notice that your VC application does not Terminate properly on termination, look for code that returns an IDispatch pointer.
For example:
COGStyle::SetIncludeBorders returns an IDispatch pointer to the object itself. This allows us to have code like this in Visual Basic. Style.SetIncludeBorders.SetValue()
With Visual Studio.NET, Wizard will generate code similar to the code shown below:
LPDISPATCH COGStyle::SetIncludeBorders(BOOL b) { LPDISPATCH result; static BYTE parms[] = VTS_BOOL ; InvokeHelper(0x11, DISPATCH_METHOD,VT_DISPATCH, (void*)&result, parms, b); return result; }
The problem with the above code is that when we make a call like the one below, the resulting temporary LPDispatch ID returned by the ActiveX as a correctly AddRef ed pointer.
pStyle->SetIncludeBorders();
What happens is that this object does not get released. We can remedy this situation by one of the following methods. Replace all occurances of LPDispatch return values with smart pointer wrappers that call Release when they are destroyed. This method has the disadvantage that AppWizard generated files will need to be modified. Have an LPDispatch that always sits on the LHS and correctly calls Release. This approach makes your code more readable, easier to follow, and in keeping with the traditions of COM, too.
LPDispatch pDispatch = pStyle->SetIncludeBorders(..); if (pDispatch) pDispatch->Release();
172
CHAPTER 13
13.1
Introduction
NOTEThis chapter is of interest to users who wish to build custom versions of the OG ActiveX controls. If you are planning to use the controls without customization you may skip this chapter.
Objective Grid for ActiveX ships with pre-built controls. We also ship several prebuilt versions of the OG 9.0 dll based on the configuration that was used to build the control. You no longer have to build the controls to use the product. Before building custom versions of the controls, decide which controls you require. Build the base control if you require just plain grid functionality. Build the ODBC, ADO, or DAO controls, depending on which of these you need in your application. If you need your control to access data across the Web, use the ADO control.
13.2
The configurations that the ActiveX Grid uses fall under the following categories: Table 12 Build Wizard Files Under Tools Folder Control ADO grid control
Ogadogrd.ocx
Definition Only ADO Database Support Only DAO Database Support Only ODBC Database Support No Database Support
ogdao.bwc
ogdb.bwc
Base grid control (ogocxgrd.ocx) or the tab control (ogocxtab.ocx) with no database support
ognodb.bwc
Out of the box we support only the four configurations specified above. In general it is possible to support other configurations (This may require additional work on your part). Objective Grid version 9.x has a plug-in architecture that makes it possible to include and exclude functionality based on need. Each of these generated sets is called a configuration. We can run the build wizard and specify a configuration and store the information in a .bwc file. We can use this configuration to let the Build Wizard generate the correct make file and build the correct .dll (with just the required components). Some Important Notes: Do not attempt to use any combination of DAO, ODBC, and ADO in the same control. The tab control no longer supports combining DAO and ODBC in the same control. In general link with the minimum OG DLL that your ActiveX will need (the default builds do this). For example, do not link the base DLL that does not support database connectivity with a grid DLL that supports database connectivity. When doing a batch build, do not combine controls that link to different variants of the grid DLL. Consequently there are no 'Release all' and 'Debug all' configurations. In short select the component one at a time and build it. If you use the default configurations, you do not have to do any extra work and the build process will link to
174 Objective Grid for ActiveX Users Guide
the correct DLL (for example, the no database connectivity control will link with the no database connectivity grid DLL). All of the above configurations create the same ogobjects dll, but these dlls are not binary compatible. When you ship these controls make sure that you ship the correct dll.
NOTEBefore building the OG ActiveX you need to have installed and properly built the Objective Grid MFC libraries. OG ActiveX version 9.0 will build with Objective Grid 9.0. It may not build with earlier version of Objective Grid. Also, you have to make sure that all the Objective Grid configurations that you will link to have been built.
The following table provides information about the supported dlls that need to be built before building ANSI Release OGX components. Table 13 Supported dlls for ANSI Release OGX components Control ADO grid control Dao grid control ODBC grid control Base grid control or the Tab control with no database support OG Supporting DLL
Og90adas.dll Og90daoas.dll Og90dbas.dll Og90nodbas.dll
NOTEWhen you install the product the OG ActiveX environment wizard should have added the following entries to the include directories in Visual C++ 6/7. Be sure to delete/move down any earlier entries that pertain to earlier versions.
Include directories added: -------------------------\\Basectl\include \\ogobjects\include \\ogadoobjects\include \\ogdaoobjects\include \\ogdbobjects\include Lib: ---\\ogobjects\lib Chapter 13 Building OCX Controls 175
13.3
Build Instructions
13.3.1 Visual C++ 6.0/7.0
NOTEPlease do not build the ActiveX controls from the projects in the basectl directory. These are shipped for reference. Use them only if you wish to browse the source.
To build, use buildw.sln located in the \build directory. The make file always makes sure that the correct supporting DLLs are built. We have all possible configuration combinations under this make file. You can build any control from this one project. 1. Open buildw.sln. 2. Select the configurations that you wish to build and do a batch build. The possible configurations are
WIN32 and UNICODE builds for
Base grid ADO control Dao control ODBC control Tab control
For example if you want to build the DEBUG version of the base grid, you would choose Win32 Debug configuration. The tab control does not support ADO, DAO and ODBC.
176
NOTEIf you wish to build the ADO control you should build the Objective Grid 9.0 ADO libraries. To build the ADO libraries you need to have the OLE DB SDK installed: (www.microsoft.com/data).
13.4
Troubleshooting
This section lists some of the common build/installation problems:
indicates that you have not built the Objective Grid DLLs for the specific configuration (ognodb in this case) that this control links to. Please build this configuration and then build the control.
then you have to install Objective Grid. If this file exists on your system but is not accessible to your build, then check the include directories under tools\\options in DevStudio to see if the Objective Grid files are in the path.
178
CHAPTER 14
14.1
Introduction
All the OLE Automation objects are implemented in an extension DLL. That leaves us with one issue. How do we get objects implemented inside an extension DLL to be used inside an OLE controller (that knows nothing about MFC extension DLLs)? The solution is to use a regular DLL server for these objects. In earlier OG ActiveX versions this was a separate server. With the present version of OGX, the objects are creatable only by the control itself, i.e., the control acts as the server for these objects. With some earlier versions, to create a COGRange object we would write :
Dim range as object Set range = CreateObject(CGXRange)
With earlier versions, control registration and maintenance was considerably more complicated and was done using a configuration wizard. Now you can use regsvr32 to register the controls.
180
CHAPTER 15
AppWizard Tutorial
Deriving from a grid
THIS CHAPTER INCLUDES: Deriving Grid Control from Base Control base class other than CGXGridCore
15.1
NOTEUnder VC.NET, the OGX AppWizard generates applications with the character set defined as Unicode (to take advantage of XP visual styles). This requires AppWizard-generated applications to link with the Unicode libraries of OGX and OG. If the applications are generated under Windows 2000, NT, or 98, the default character set can be changed back to ANSI.
The Microsoft Knowledge Base article number Q141489 gives details on how an OLE control that is COleControl-based can be derived from another control. We strongly recommend this Knowledge Base article before continuing to read this chapter, which does not repeat information from this article. We have also provided a wizard that will generate a skeletal template you can use for this purpose. The main areas of change are in the dispatch maps and the IDs used for derived controls. You can use the AppWizard that ships with the ActiveX version to generate a skeletal control that derives from the base grid control. This wizard is installed on your system when you install the OG ActiveX version. When you select File|New|Project to create a new Workspace, you will see the Objective Grid for ActiveX App Wizard. This wizard only helps with generating base control derived controls. If you wish to generate data bound controls, you can still use this wizard but will have to manually perform many of the steps given below. 1. Launch Visual Studio.NET and choose File|New|Project .
182
5. Now click on the Change Path button and proceed to change the path with the displayed dialog. Figure 52 Objective Grid ActiveX AppWizard Select directory
6. Please ensure that the root folder of your ActiveX installation appears in the Chosen Combo box. 7. Click Open. 8. After you have changed the path, you can click Finish button in the AppWizard dialog to generate the files. Please note that the wizard does not validate the path that you have specified. If you specify an incorrect path, it would generate files just the same. When building, the build would complain that one or more files cannot be found. You can correct the path and point to the files at this point, too.
NOTEIf you copy the generated project to a different machine or if your OG ActiveX installation changes, you will have to make sure that absolute paths in the .cpp files that are generated are updated.
184
15.2
As you can see from the above code, changing the base class just involves changing the typedef. For example, to change the base class to a browser grid, just add _CCEBROWSER to the pre-processor settings.
NOTEBased on the base class you use, there will be additional work required to implement the complete control.
Typical scenarios for deriving from the OG ActiveX controls: You want to add a new control type. You want to add a method. You want to add a property. You want to add an event. Controls types cannot be added in Visual Basic code. For example, if you subclass CGXEditControl, you can then derive your own control that can use this derived CGXEditControl, instead of the default CGXEditControl. The MFC grid supports hosting any arbitrary CWnd-based control inside the grid using CGXWndWrapper. This can be done inside the OG ActiveX controls only by deriving from them and then registering the window wrapper control in the derived code.
186
CHAPTER 16
Error Handling
16.1
Introduction
We have implemented different kinds of error handling for automation methods and events. For automation methods, our default error handling uses the COleControl function ThrowError, which halts execution of the offending function and enables easy tracking of errors in methods. For example:
DECLARE_ROWCOL(nCol) short s(0); try { DELEGATE_BROWSEMETHOD_RETVAL(s, GetFieldFromCol(xnCol)) } catch(...) { ThrowError(GXCTL_E_INTERNALERROR, IDS_OG_VBINTERNALERROR); } return s;
For events, however, this kind of error handling is not advisable. One reason is that events are almost always fired by the object in a manner that is beyond the control of the program. If we were to pop up a dialog when an error occurred in GetStyleRowCol, many message boxes would appear in succession (FireError does not halt execution). Instead, we decided to use Structured Exception Handling (SEH). Using SEH we simply fire an error event that will be handled at the discretion of the VB programmer. The severity of the error is also passed on as a parameter. Typical error handling code for events is shown below.
try { if (xbool) return COGOleGrid::OnGridSysDeadChar(nChar, nRepCnt, nFlags); else return TRUE; } catch(COGSEH_Exception& e) { Chapter 16 Error Handling 187
// Generate a custom error event and leave it to the discretion of // the VB programmer FireGridError(eventidOnGridSysDeadChar,e.getSeHNumber(),!e.getExcep tionFlags()); return FALSE; }
188
CHAPTER 17
Interfaces
Brief Introduction to COM Adding Controls to
17.1
Introduction
NOTEYou can skip this chapter if you have a working knowledge of COM/ ActiveX controls.
17.2
Because COM is independent of any programming language, a COM object created in Visual C++ can be used by a client application written in Visual Basic (or in any other such environment), and vice versa. This makes the component far more accessible to a wider variety of applications than was earlier possible and is one giant leap further in achieving extensive code reuse.
190
If we could get rid of these dependencies in some manner, then the DLL architecture would not be so bad. After all, Windows itself is a set of DLLs. COM takes a kind of mixed approach to get around this scenario. This approach is best explained in C++ terms. If the header file that we are linking to is an abstract base class, the compiler would no longer look for an implementation of the members. We just export one function that creates and returns an object of a derived type. COM uses a more flexible approach; the returned object is a Class Factory, which is capable of creating objects that we need. But basically the approach is the same. We can then use the created object and the interface to which we already have access to use the object without any problems. We can also very easily load the library dynamically using LoadLibrary since there is only one entry point for which we need to get the address. Anyone who has loaded huge DLLs that have a trillion entry points will understand the benefits that this gives us. (Loading the library dynamically helps us reduce compile time dependencies.) This is exactly how COM works. COM expects components (inproc ones) to export a function called DllGetClassObject that is capable of returning an object that can create instances of the object that the client needs. Once the client has this object it can simply use the object since it already knows that it implements a specified abstract class (in OLE terms this abstract class definition is called an Interface). The only remaining piece in the puzzle is the manner in which COM loads the actual server. This is where there is some operating system-specific stuff that needs to be done (remember COM is not specific to Windows). The OS on which COM is implemented would provide some sort of API which, given a unique identifier (a GUID in the COM world), would load the server into memory (under Windows the information required to load the server is stored in the registry) and then call DllGetClassObject. DllGetClassObject is applicable for InProcServers only. COM also provides for out-of-process servers but these do not concern us at this point since our controls and most other ActiveX controls are InProc servers. That is all there is to it. Fundamental to the COM mechanism is the IUnknown interface, which is implemented by all COM-based objects. This interface is uniquely identified on every machine by {00000000-0000-0000-C000-000000000046}. IUnknown must expose the following three methods in the order given: QueryInterface(), AddRef() and Release(). In the next chapter we will look at this important interface in more detail. A client using a COM component first gets the components class identifier (CLSID). Then the client creates an instance of the component (with the help of OS-provided APIs). Now the client can probe the component to discover what interfaces it supports and call the method for a desired operation. A single server can support any number of interfaces (we can think of it as inheriting from several abstract base classes in C++ terms).
NOTEAn OLE object registers its CLSID in the system registration database (i.e., registry), so that it can be loaded and programmed by other applications. The CLSID associates the component object with a DLL or EXE file.
17.3
192
OLE/ActiveX controls depend on a lower-level technology called COM, which defines language-independent, binary standards for interaction among objects. Essentially, the container (Visual Basic in this case) knows nothing about the controls other than that they support certain actions. It can then proceed to ask them to perform these actions. To be good citizens of the OLE/ActiveX control world, the controls should implement all the required functionality that the container can then access through predefined interfaces. For example, if the container expects controls to move as it wants them to, then it would probably require them to implement a Move interface through which it can call the controls and ask them to move to the location that it specifies. In broad terms though, the container and the control know nothing specific about each other. They can understand each other only through certain terms that are predefined (usually by Microsoft).
NOTEYou are free to define your own interfaces, but of course MS Visual Basic will not support them.
17.4
Interfaces
Let us look at interfaces in a little more detail. Interfaces are simply groups of functions. They can be implemented in any manner that the implementor sees fit, but they must have a predefined signature. In simple terms, COM looks at any object as a blob (binary large object) of data that can perform some actions through a predefined interface. The blob is capable of handing any other blob that cares to ask a pointer to a table of pointers to functions that implement defined interfaces. In C++ this table is known as the virtual function table (VTBL). Using this pointer, the blob that asked for the pointer (the client blob) can then call the object and ask it to execute the functions.
Blob1 (Server)<--------- (Do you support Interface Move?)--------------Blob2 (Client) Blob1------------------------> Blob2 (Yes, I do. Here is my pointer to the table of functions that implement the Move interface.)
The important part is that both blob 1 and blob 2 have the same idea about the Move interface. As long as both do, they are home free, and blob 2 can manipulate blob 1 using the pointer under the terms of the Move contract interface. In C++ terms, an interface is usually defined as an abstract class. For example, Move could be defined as class Move.
By convention, interfaces begin with an I. Therefore, abstract class Move would be IMove in the interface world.
class IMove { MoveMe(int x, int y) = 0; HideMe(BOOL b) = 0; };
Now, if we were to implement a blob that implements this interface, we could derive from IMove and implement these functions.
class ImplementMove: public IMove { MoveMe(int x, int y); HideMe(BOOL b); };
The actual implementation is up to the developer (the implementor). When blob 2 (the client) wants to know whether we support IMove, we can say Yes. The manner in which we are required to say this is also predefined. In fact, any blob that has to talk to another blob must implement a Microsoft-defined interface called IUnknown. This interface has three methods: QueryInterface - Do you support this interface? AddRef Release AddRef and Release have to do with the implementation, and we will discuss the specifics a little later. When blob 2 calls blob 1 with QueryInterface, it will ask whether it supports the interface IMove. Then blob 1 can say Yes and return a pointer to blob 2 that will have all the pointers to the various functions that Move needs implemented. In C++ terms, this would just be the this pointer (in this particular case). Once blob 2 (the client) has this pointer, it can then call the functions in Move.
pointer->MoveMe(0,0);
Containers like Visual Basic (and most ActiveX containers) operate through a more complex handshaking process and also hide this completely from the programmer. But this idea is the core behind this process. Now we know that, for blob 2 to know whether blob 1 supports IMove, all it has to do is ask through QueryInterface. But how does blob 1 (the server) get created in the first place so that blob 2 can ask about IMove? This is where COM steps in with default support (This support is OS specific as we mentioned in the previous chap194 Objective Grid for ActiveX Users Guide
ter). Under Windows, COM will search the registry for information on the program that is capable of creating blob 1 when blob 2 asks it to create blob 1 (blob 2 will do this with a call to CoCreateInstance in most cases. CoCreateInstance is a COM API that is implemented on each OS that COM is supported on). There is actually a further level of indirection involved with class factories coming into the picture. Essentially COM steps in and takes care of the other work and finally can hand blob 2 a pointer to the requested interface or to IUnknown. Blob 2 can then use IUnknown to ask blob 1 about any other interfaces that it supports and so on. Since COM takes care of loading the server that serves up blob 1, it should take care of unloading it when it is no longer needed. It is in this context that reference counting becomes important. Any pointer that the object gives out will cause an increase (reasonably so, this pointer will in all likelihood be used at some point and we do want the object to be around when the pointer is used) in the reference count (this is where AddRef comes in). The user of that pointer will then call Release when it is done with the pointer (the interface). When the reference count becomes zero, the number of users using that interface is zero. When none of the interfaces that the object supports are in use, it is then free to unload itself. DLL servers typically accomplish this through a function called DllCanUnloadNow that COM (the implementation of COM on the OS actually) calls every now and then. When the DLL server knows that it's no longer required to remain in memory, it returns OK and COM unloads the server. ActiveX controls implement several of these predefined interfaces through which containers can interact with the controls. Similarly, controls can interact with containers through such defined interfaces. Thus two-way interaction between the controls and the container takes place. Essential interfaces that controls implement. (It is no longer required that controls implement these interfaces. The only required interface is IUnknown.) IConnectionPointContainer IConnectionPoint IEnumConnectionPoints IEnumConnections IDataObject IOleCache IOleControl IOleInPlaceActiveObject IOleObject IPerPropertyBrowsing
IPersist IPersistMemory IPersistStorage IPersistStreamInit IPersistPropertyBag IProvideClassInfo IProvideClassInfo2 ISpecifyPropertyPages IViewObject IViewObject2 These interfaces are documented in the VC online reference as well as in the reference books mentioned in Section 17.2.2. A knowledge of these interfaces is not essential. However, do refer to the interface specifications as you come across them. When developing ActiveX controls with MFC, we get a default implemetation for most of these interfaces. Therefore our work as a control implementor is largely the customization of these implementations to cater to our needs. The MFC class that implements an ActiveX control is COleControl. The OG ActiveX implementation derives from this class and adds grid funtionality (similar to the manner in which we add grid funtionality to a CWnd or a CView) .
196
Index
Symbols .ogl file 99 A AppWizard 181 Automation Objects 114 B base styles definition 8 bound mode of OG Designer 87 build wizard 173 building OG ActiveX controls 173 C CalcSumOfRowHeights 136 cells definition 9 definition of covered 9 definition of current 9 CGXGridCore SetStyleRange() 120, 125 CGXRange 125 SetCells() 125 SetCols() 125 SetRows() 125 SetTable() 125 CGXStyle 120 attributes 124 Check List Combo box 42 CheckBox 40 CheckBox, 3D Effect 40 Client machine 84 COGData 130 COGFont 130 COGStylesMap 131 columns,sorting 61 COM 189 ComboBox Display Choice, One Based 41 Combobox No Text Fit 40 ComboBox One Based 41 Combobox Text Fit 40 ComboBox Zero Based 41 ComboBox, Display Choice, Zero Based 41 comctrl32.dll 5 common terms 8, 10 Component Object Model 189 control definition 9 control child definition 9 covered cells definition 9 CreateAdoParamObject 115 CreateBoolStateObject 115 CreateBrushObject 115 CreateBStrStateObject 115 CreateDaoDatabaseObject 116 CreateDaoParamObject 115 CreateDaoRecordsetObject 116 CreateDatabaseObject 116 CreateDataObject 116 CreateDbParamObject 115 CreateDoubleStateObject 115 CreateFontObject 116 CreateHintObject 116
Index 197
CreateLongStateObject 115 CreateParamObject 115 CreatePenObject 116 CreatePointObject 116 CreatePrintInfoObject 116 CreatePropertiesObject 116 CreateRangeListObject 115 CreateRangeObject 115, 126 CreateRecordsetObject 116 CreateRectObject 116 CreateSizeObject 116 CreateSortInfoObject 116 CreateStyleObject 115, 118 CreateStylesMapObject 115 Currency control 43 current cell definition 9 D data binding 4 data source definition 9 Data Source, System 71 Data Source,File 64 Database server 84 database, registering as data source 63 Date time control with calendar 42 Date time control without calendar 42 deployment file 111 documentation conventions 8 formats 7 dropdown list, changing content at run time 54 E Edit control 39 Edit Control With Spinner 40 Edit with Hot Spot 40 Edit with Scrollbar 40 Enter key, changing behavior of 37
198 Objective Grid for ActiveX Users Guide
error handling 187 event, definition 23 events, handling 168 F File Data Source 64 G GetCurrentEventFirer 57 glossary 8 GridCommitTrans 135 Gridocx object 74 GridOpenRecordset 74 GX_IDS_CTRL_CBS_DROPDOWN 41 GX_IDS_CTRL_CBS_DROPDOWNLIS T 41 GX_IDS_CTRL_CBS_TABBED_DROP DOWN 41 GX_IDS_CTRL_CBS_TABBED_DROP DOWNLIST 42 GX_IDS_CTRL_CHECKBOX 40 GX_IDS_CTRL_CHECKBOX3D 40 GX_IDS_CTRL_CHECKLIST_COMBO BOX 42 GX_IDS_CTRL_COMBOBOX 40 GX_IDS_CTRL_CURRENCY 43 GX_IDS_CTRL_DATETIME 42 GX_IDS_CTRL_DATETIMENOCAL 4 2 GX_IDS_CTRL_EDIT 39 GX_IDS_CTRL_HEADER 40 GX_IDS_CTRL_HOTSPOT 40 GX_IDS_CTRL_LISTBOX 40 GX_IDS_CTRL_MASKEDIT 43 GX_IDS_CTRL_ONEBASED 41 GX_IDS_CTRL_ONEBASED_EX 41 GX_IDS_CTRL_PUSHBTN 39 GX_IDS_CTRL_RADIOBTN 39 GX_IDS_CTRL_RADIOBTN3D 40 GX_IDS_CTRL_SCROLLEDIT 40 GX_IDS_CTRL_SPINEDIT 40
GX_IDS_CTRL_STATIC 39 GX_IDS_CTRL_TEXTFIT 40 GX_IDS_CTRL_ZEROBASED 41 GX_IDS_CTRL_ZEROBASED_EX 41 gxApplyNew 121 gxCopy 121 gxExclude 121 gxOverride 121 H Header 40 HorizontalSorting 61 I IIS server machine 84 InitRange 127 installation directory 5 Interface 191 IUnknown 191 L layout file 98 licensing 112 link error 177 ListBox 40 M Masked Edit control 43 MFC Style Tabbed Combo box 41 MFC Style Tabbed Drop Down List 42 N naming conventions 110 O Objective Grid Designer 87 components 90 Objective Grid for MFC 1 ODBC Data Source Administrator 63 OGAdoParam 113, 115 OGBoolState 115, 132 OGBrush 115, 129
OGBStrState 115 OGDaoDatabase 116 OGDaoParam 113, 115 OGDaoRecordset 116 OGData 116 OGDatabase 116 OGDatabase object 74 OGDbParam 113, 115 OGDoubleState 115 OGFont 116 OGGridParam 113, 115 OGHint 116 OGLongState 115 OGPen 116 OGPoint 116 OGPrintInfo 116 OGProperties 116 OGRange 113, 115 OGRangeList 115 OGRecordset 116 OGRecordset object 74 OGRect 116 OGSize 116 OGSortInfo 116 OGStyle 113, 115, 117 OGStylesMap 115 OLE Automation 189 OnGridInitialUpdate 132 P Parameter object 113 parameter object 117 PreInitialUpdate 61 print preview, not available in Tab version 57 properties definition 9 properties, definition 23 Push Button 39
Index 199
R Radio Button 39 Radio Button, 3D Effect 40 range definition 9 Range object 113 range objects 125 ranges, creating 126 ranges, initializing 127 recordset 74 registering a component 111 registration failing 177 S sample AdoQuery 102 CAB,troubleshooting 106 Formula 103 Gridapp 103 Internet 102 ODBC 104 Preview 105 Tab 57 TabBase 105 scrollbars, activate and de-activate 56 SetCells() 125 SetCols 127 SetCols() 125 SetControl method 39 SetIncludeInterior() 118 SetIncludeSize 131 SetInterior() 117, 118 SetRows 127 SetRows() 125 SetScrollBarMode 56 SetSize 131 SetStyleRange() 120, 125 SetTable() 125 Solution Services Group 10 sorting columns 61 Static text control 39
Structured Exception Handling. 187 style architecture 120 definition 9 object 120 Style object 113 creation 118 style objects 117120 style properties,setting 118 System Data Source 71 System DSN 64 T terms,common 8 Trouble shooting 177 U Undo/Redo 135 Unicode 181 unresolved externals 177 V VC.NET 4, 181 VerticalSorting 61 virtual functions 124 virtual mode 122 Visual Studio.NET 4 W Windows (MFC Style) Combobox 41 Windows (MFC Style) Drop Down List 41 Windows XP 5 visual styles 5 workbook definition 10 worksheet definition 10 wrapper class, generating 167
200
Index 201
Index 202