Smart Grid

Environment: Windows NT4 (SP6), Visual C++ 6 (SP2)

Introduction

This is a data grid built using ATL 3.0, STL and Win32 API. The article provides some explanation about the grid's capabilities. I will not describe in detail the implementation of my grid because there is a lot of code and I do not think that anybody would have enough patience to read something like that. Source code is commented and this should be enough.


Click here for larger image

Data types

As one can see every column of the grid has an associated data type. This means that every value in the same column will have the same type. Available types are:
	
// These are the data types
typedef [v1_enum] enum DataType 
{
 DataType_Empty = 0,      // Not specified
 DataType_Null = 1,       // NULL
 DataType_I2 = 2,         // 2-byte signed int
 DataType_I4 = 3,         // 4-byte signed int
 DataType_R4 = 4,         // 4-byte real
 DataType_R8 = 5,         // 8-byte real
 DataType_Cy = 6,         // Currency
 DataType_Date = 7,       // Date
 DataType_BStr = 8,       // Binary string
 DataType_Dispatch = 9,   // IDispatch FAR*
 DataType_Error = 10,     // Scodes
 DataType_Bool = 11,      // Boolean; True=-1, False=0
 DataType_Variant = 12,   // VARIANT FAR*
 DataType_Unknown = 13,   // IUnknown FAR*
 DataType_UI1 = 17,       // Unsigned char	

// My types
DataType_Option = 0x0100, // Combo box
DataType_StrictOption     // Drop down list

} DataType;   

As you can see, these types are in fact almost all VARTYPEs. If the user tries to change the value of a cell with a value whose type is incompatible with the type associated with the column, the value of the cell will not change. I also added some custom types, like Option and Strict Option. Data of this types are edited using combo boxes and drop down lists, as you can see in the next picture.

Range specifications

Every column has a type and a range for values. This means that you can enter in the cells values that have the type associated with the column and are in the specified range. For example you can specify ranges for numeric values:"18,49" or you can specify options that are available for an Option or StrictOption type: "Male|Female". For numeric values the posibilities to specify ranges are:

  • ",nMax" - This means that values in the column are restricted to be less or equal than nMax value
  • "nMin," - Values in the column are restricted to be greater or equal than nMax value
  • "nMin,nMax" - Values in the column cannot be changed by the user in order to be outside of the interval [nMin,nMax]
  • User operations

    Row selection is also available. You can select multiple rows of the grid using right-click. Clicking the upper-left corner of the grid selects all rows in the grid. Double clicking the same area unselects all lines. From VB, for example, you can test if a row is selected or not. Optionally, rows can be numbered on the left header. Pressing enter or F2 enters the grid in edit mode. Depending on type of the data that is in the selected cell, on edit mode opens an edit control, combo box, drop down list or the value is automatically changed ( boolean values ). Sorting is available. If a column's property AllowUserToSort is set on TRUE, then double-clicking that column sorts the rows after the values on that column. If the user modifies that column and the content becoms unsorted, pressing F5 makes a resort.

    Static and dynamic resizing is available for top header height, left header width, row height and column width. In the previous image I have marked with red dots the points that can be used for resizing.

    Colors are fully customizable. You can choose your favorite combination of colours for grid items, as you can see in the screenshot.

    Data sources

    You can fill the grid with data from code (VB is the easiest way to manipulate this grid). Also, there are two more ways to enter data into the grid: a previously opened ADO Recordset or a connection string and a SQL SELECT statement. Unfortunately, in this version of the grid the databases are not automaticaly updated when the values of the cells change. This will be done in the next version.

    	
    // This are data source types
    typedef [v1_enum] enum
    {
     DataSourceType_None = 0,
     DataSourceType_ADORecordset,
     DataSourceType_SQLStatement,
    } DataSourceType;
    
    

    Database connection is not very well implemented yet, so I recommand this grid to be used with DataSourceType_None.

    Grid manipulation

    I will describe now the interfaces that can be used to manipulate grid elements.

    ISmartGrid

    	
    // Use this to manipulate grid properties
    interace ISmartGrid : IDispatch;
    

    ISmartGrid interface is used to manipulate general poperties of the grid. ISmartGrid is a big interface so I will describe its methods and properties whitout inserting the IDL code here.

    Methods

    AddColumn( [in] Name, [in] Type, [in, optional] ReadOnly, 
               [in, optional] Range, [out, optional] Column )
    
    InsertColumn( [in] Name, [in] Type, [in] Position, 
                  [in, optional] ReadOnly, [in, optional] Range, 
                  [out, optional] Column )
    
    RemoveColumn( [in] Position )
    
    AddRow( [out, optional] Row )
    InsertRow( [in] Position, [out, optional] Row )
    RemoveRow( [in] Position )
    
    SelectAllRows()
    UnselectAllRows()
    Requery()
    

    Properties

    // State
    ActiveColumn
    ActiveRow
    NumberOfColumns
    NumberOfRows
    ReadOnly
    UILocked
    		
    // Data access
    Column
    ConnectionString
    CursorType
    DataSource
    DataSourceType
    LockType
    Row
    		
    // User interface
    ActiveRowColor
    ContentBkColor
    		
    		
    ContentForeColor
    ContentLinesColor
    		
    DefaultColumnWidth
    Font
    HeaderBkCOlor
    HeaderForeColor
    HeaderLinesColor
    LeftHeaderVisible
    LeftHeaderWidth		
    RowHeight
    SelectedItemColor
    TopHeaderHeight
    TopHeaderVisible			
    

    Using methods from ISmartGrid interface can be added columns and rows to the grid. When adding a column or a row, a reference to it is optionally returned, in order to be used immediatelly after it is inserted. I split the properties of the grid in three sections. One is called State and I grouped here all properties that refers to the state of the grid. The second group refers to Data Access and the third group of properties can be used to customize User Interface.

    ICell

    	
    [
     helpstring("ICell interface is used to access cells"),
     uuid(C6462BB2-E639-11d3-8138-0000B49DBD2E),
     pointer_default(unique)
    ]
    interface ICell : IUnknown
    {
     [propget, helpstring("property Value")] 
     HRESULT Value([out, retval] VARIANT *pVal);		
     [propput, helpstring("property Value")] 
     HRESULT Value([in] VARIANT newVal);
    };
    

    As you can see ICell interface is designed to manipulate one cell. A cell has a Value property that is used to read or set the value of the cell.

    IRow

    [
     helpstring("IRow interface used to access rows from grid"),
     uuid(E18D7A91-E639-11d3-8138-0000B49DBD2E),
     pointer_default(unique)
    ]
    interface IRow : IUnknown
    {		
     [propget, helpstring("Get the specified cell of this row")] 
     HRESULT Cell(long nIndex, [out, retval] ICell **pVal);
    
     [propget, 
     helpstring("Returs TRUE if this row is active and false otherwise")] 
     HRESULT Active([out, retval] BOOL *pVal);
     
     [propput, 
     helpstring("Put this property TRUE if you wanna activate this row")] 
     HRESULT Active([in] BOOL newVal);
    
     [propget, 
     helpstring("TRUE if this row is selected")] 
     HRESULT Selected([out, retval] BOOL *pVal);
     
     [propput, 
     helpstring("Put this property on TRUE if you want to select this row")] 
     HRESULT Selected([in] BOOL newVal);
    
     [propget, helpstring("Returns the index of this row")] 
     HRESULT Index([out, retval] long *pVal);
    };
    

    IRow interface can be used to manipulate each row. A row is composed from cells. You can get a reference to a cell from a given row using the column index. Only one row can be active at a time. This row can be retreived or change through Active property. One or more rows can be selected. You can select or find if a row is selected through Selected property. The index of a row is accesible for reading by Index property.

    IColumn

    	
    [
     helpstring("IColumn interface used to access columns individually"),
     uuid(506580C1-E643-11d3-8138-0000B49DBD2E),
     pointer_default(unique)
    ]
    interface IColumn : IUnknown
    {
     [propget, helpstring("Access a cell from this column")] 
     HRESULT Cell(long nIndex, [out, retval] ICell** pVal);
    
     [propget, helpstring("Get the width of the column in pixels")] 
     HRESULT Width([out, retval] long *pVal);
     [propput, helpstring("Put the width of the column in pixels")] 
     HRESULT Width([in] long newVal);
    
     [propget, helpstring("Get the label of the column")] 
     HRESULT Name([out, retval] BSTR *pVal);
     [propput, helpstring("Put the label of the column")] 
     HRESULT Name([in] BSTR newVal);
    
     [propget, helpstring("Get data type used for this column")]
     HRESULT DataType([out, retval] DataType *pVal);
    
     [propget, helpstring("Put the values range")] 
     HRESULT Range([out, retval] BSTR *pVal);
     [propput, helpstring("Get the values range")] 
     HRESULT Range([in] BSTR newVal);
    
     [propget, helpstring("Get the index of the column")] 
     HRESULT Index([out, retval] long *pVal);
    
     [propget, helpstring("Get the ReadOnly state of the column")] 
     HRESULT ReadOnly([out, retval] BOOL *pVal);
     [propput, helpstring("Changes the ReadOnly state of the column")] 
     HRESULT ReadOnly([in] BOOL newVal);
    
     [propget, helpstring("Get SortOrder property of this column")] 
     HRESULT SortOrder([out, retval] SortOrder *pVal);
     [propput, helpstring("Changes the SortOrder property of the column")] 
     HRESULT SortOrder([in] SortOrder newVal);
    
     [propget, helpstring("Returns AllowUserToSort value")] 
     HRESULT AllowUserToSort([out, retval] BOOL *pVal);
     [propput, helpstring("Sets AllowUserToSort value")] 
     HRESULT AllowUserToSort([in] BOOL newVal);
    
     [helpstring("Resorts the grid data, by this column content")]
     HRESULT Resort();
    };
    

    IColumn interface can be used to read or change properties of each column. Like for rows, you can access cells from a column through Cell property if you specify index of the cell in column. A column has the following properties: Width in pixels, Name which is the label displayed on the top header, DataType is read-only property used to retreive data type associated with the column. Index is the zero-based index of the column within the grid and ReadyOnly boolean property which can be used to make a column available only for view, even the grid is not entirely ReadOnly.

    Events

    There are a few events that the grid raises when certain operations takes place. This events can be seen in the following dispinterface:

    	
    [
     uuid(855D88AF-C9BD-11D3-A34E-0080AD303A9A),
     helpstring("_ISmartGridEvents Interface")
    ]
    dispinterface _ISmartGridEvents
    {
    properties:
    
    methods:
     [id(1), helpstring("Raised when the active row has changed")] 
     HRESULT RowChanged(long nPrevious, long nNew);
    
     [id(2), helpstring("Raised when the active column has changed")] 
     HRESULT ColumnChanged(long nPrevious, long nNew);
    
     [id(3), helpstring("Raised when the active cell has changed")] 
     HRESULT CellChanged(long nPreviousRow, long nPreviousColumn, 
                         long nNewRow, long nNewColumn);
    
     // Cell content changing events
     [id(4), helpstring("method BeginCellEdit")] 
     VARIANT_BOOL BeginCellEdit(long nRow, long nColumn, ICell*);
     [id(5), helpstring("method EndCellEdit")] 
     HRESULT EndCellEdit(long nRow, long nColumn, ICell*);
     [id(6), helpstring("method CellUpdated")] 
     HRESULT CellUpdated(long nRow, long nColumn, ICell*);
    };
    

    I have tested this dll using Microsoft Visual Basic 6.0. The archive contains the VB project that I used to test this ActiveX control.

    This control does not depends on MFC.

    Version History

    • 2000/05/11 - Original Post
    • 2000/05/18 - Added a lot of functionality like sorting, combo-boxes, sorting capabilities, event firing and bug fixes.


    Comments

    • Why use droplist combobox in "Male|Female" then error ?

      Posted by Legacy on 09/30/2003 12:00am

      Originally posted by: Original P

      Thank you
      for answer

      Reply
    • exGrid

      Posted by Legacy on 06/12/2002 12:00am

      Originally posted by: Mike Philips

      Great work Alex, 
      
      

      For those who want to check, here's another ATL grid

      http://www.exontrol.com/sg.jsp?content=products/exgrid

      Regards,
      Mike

      Reply
    • Firing of events for datatype Boolean

      Posted by Legacy on 12/05/2001 12:00am

      Originally posted by: willems

      Very cool.
      During editing, events only seem to fire for edit controls and not check boxes? Event eg. OnCellUpdated doesn't fire for Boolean datatypes? Or am I missing something?

      Reply
    • Using this grid in another ATL controls property page

      Posted by Legacy on 11/12/2001 12:00am

      Originally posted by: Scott Tomer

      I'm trying to use this grid in a property page of my own ATL control.

      Have you tried this?

      I'm having trouble. Especially with the option datatype, when I make a selection it crashes.

      If you have an example project using this, I would greatly appreciate any help you can provide.

      Reply
    • Compile error

      Posted by Legacy on 10/03/2001 12:00am

      Originally posted by: alan

      After compiling the Win32 Debug version I get this error

      SmartGrid.h(373) : error C2668: 'InlineIsEqualGUID' : ambiguous call to overloaded function

      why

      Reply
    • please add a vc sample!!

      Posted by Legacy on 09/12/2001 12:00am

      Originally posted by: tangtao

      tks!!

      Reply
    • #import doesn't work

      Posted by Legacy on 06/07/2001 12:00am

      Originally posted by: Rolf Behrens

      I want to use the control in a VC project, and import it via
      
      #import "..\AlxGrd.tlb"
      A lot of errors occurs. So I took a look at the sources.

      With the following changes in AlxGrid.idl it was compiled properly.
      ________
      typedef enum GridMode {
      GridMode_MergeCells = 0,
      GridMode_NotMergeCells
      } GridMode;
      and
      ______________
      typedef [v1_enum] enum DataSourceType
      {
      DataSourceType_None = 0,
      DataSourceType_ADORecordset,
      DataSourceType_SQLStatement,
      } DataSourceType;

      and
      _________
      typedef [v1_enum] enum SortOrder
      {
      SortOrder_None = 0,
      SortOrder_Ascending,
      SortOrder_Descending,
      } SortOrder;


      CU Rolf

      Reply
    Leave a Comment
    • Your email address will not be published. All fields are required.

    Top White Papers and Webcasts

    • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

    • In this webinar, IDC featured speaker Steve Conway, Vice President of High Performance Computing, will present an update on the global x86 HPC cluster market. The presentation will include IDC's five-year forecast for the medium- to large-scale technical computing and data analysis emerging markets by systems, processors and application middleware. Cray's featured speaker, John Lee, Vice President of Cray Cluster Advanced Technology Systems, will present the new Cray® CS400™ cluster series based on …

    Most Popular Programming Stories

    More for Developers

    Latest Developer Headlines

    RSS Feeds