Click here to Skip to main content
Click here to Skip to main content
Go to top

Tree control and Buttons for MFC Grid control

, 25 Apr 2001
Rate this:
Please Sign up or sign in to vote.
A set of classes derived from CGridCtrl that embed a tree control, button controls, and virtual cells within the grid
<!-- Article Starts -->

Grid w/ Tree + Buttons

Implement IE-Style Option Dialog

Introduction

This example demonstrates how to embed a tree and add button cells to Chris Maunder's MFC Grid control (derived from CWnd).

The example derives classes from CGridCellBase that implement different types of cells. The types of cells are as follows:

  • Cells that draw a tree
  • Cells that draw one or more controls
  • Cells that draw a tree + controls
  • Virtual cells - data not stored in each cell

By working with CGridCellBase, you can create all sorts of effects without altering the MFC Grid source files. The example avoids deriving a new grid class. Instead, it dynamically replaces cells within a basic CGridGtrl instance. This should minimize the pain of adding capabilities to your existing grid implementations.

(Note that users of the Grid control are bound by Chris' copyright requirements detailed on his web page.)

Files

The example consists of a number of files stored in various directories:

GridBtnCell_src
  BtnDataBase.* A single instance holds common cell properties for all cells of type CGridBtnCell and CGridBtnCellCombo
  GridBtnCell.* The example uses instances of these cells in columns 3 thru 6 to draw buttons
  GridBtnCellCombo.* Column 7 has instances of these. Uses InPlaceList for editing. It is derived from CGridBtnCell
  GridBtnCellBase.* Base class. CGridBtnCell derived from this.
  InPlaceList.* Used by GridBtnCellCombo to provide drop-down list control for editing
GridCtrl_src
  *.* Copy of MFC Grid source code
GridTreeBtnCell_src
  GridTreeBtnCell.* Tree + control cell combination. Used by "Options" Dialog. It is derived from CGridTreeCell and contains an instance of CGridBtnCell
TreeColumn_src
  GridTreeCell.* The example uses instances of these cells in column 2 to draw a tree
  GridTreeCellBase.* Base class. CGridTreeCell derived from this.
  TreeColumn.* A single instance holds common cell properties for all cells of type CGridTreeCellBase. Manages a collection of CGridTreeCellBase's in a grid column to form an embedded tree

How to Use

Each type of grid requires a slightly different setup. (See the MFC Grid page for information on how to use the MFC grid.)

To Setup Cells that Draw a Tree

Add an instance of the following to the header file of the CDialog or CView that will host the grid:

// (.h file)

    CGridCtrl m_Grid;           // the grid
    CTreeColumn m_TreeColumn;   // provides tree column support

In the CDialog or CView implementation, setup an array of UCHARs that define the tree indentation level. 1=root, 2=first branch off root, etc.

// (.cpp file)

    unsigned char ucPatternAry[] = { 1,2,3,4,4,5,5,3,3,4,2,3,4};

In the CDialog or CView implementation, specify grid column information, then use CTreeColumn::TreeSetup() to define grid rows tree placement. For CDialog, you may want to do this in the OnInitDialog() member.

// (.cpp file)

    m_Grid.SetColumnCount( 4);
    m_Grid.SetFixedColumnCount( 1);

    m_TreeColumn.TreeSetup( &m_Grid, // tree acts on a column in this grid
                        2,           // which column has tree
                        sizeof( ucPatternAry), // total number of rows in the
                                               //  tree if totally expanded
                        1,           // Set fixed row count now, too
                                     //  Grid total rows=aiTotalRows+aiFixedRowCount
                        ucPatternAry,// Tree Level data array --
                                     //  must have aiTotalRows of entries
                        TRUE,        // T=show tree (not grid) lines; F=no tree lines
                        TRUE);       // T=use 1st 3 images from already set image list
                                     //  to display folder graphics

You can modify the existing tree structure with calls to CTreeColumn's InsertTreeBranch() and DeleteTreeBranch(). (Calling TreeSetup() with different tree configurations was the only way to change the tree structure with the original implementation, but had the disadvantage of destroying cell contents.)

The example demonstrates the inserting and deleting of branchs and some additional capabilites of the tree. See CTreeColumn's member functions for more information.

To Setup Cells that Draw Buttons

Add an instance of the following to the header file of the CDialog or CView that will host the grid:

// (.h file)

    CGridCtrl m_Grid;           // the grid
    CBtnDataBase m_BtnDataBase; // holds common properties for button cells

Associate the grid with the cell property holder in the implementation file of the CDialog or CView.

// (.cpp file)

    m_BtnDataBase.SetGrid( &m_Grid);

You may want to copy an existing cell's properties to act as the model for all button cells. You could do this just one time.

// (.cpp file)

    // retain old cell properties
    CGridBtnCell GridCellCopy;
    GridCellCopy.SetBtnDataBase( &m_BtnDataBase);
    CGridCellBase* pCurrCell = m_Grid.GetCell( row, col);
    if (pCurrCell)
        GridCellCopy = *pCurrCell;  // data will be stored in m_BtnDataBase

After the grid's window created, replace default cells with button cells as required. The following code is called in CDialog::OnInitDialog().

// (.cpp file)

    m_Grid.SetCellType( row, col, RUNTIME_CLASS(CGridBtnCell) );
    CGridBtnCell* pGridBtnCell = (CGridBtnCell*)m_Grid.GetCell( row, col);

    // tell new cell where to get its property data
    pGridBtnCell->SetBtnDataBase( &m_BtnDataBase);

Setup controls within new button cell. The example implementation allows up to 4 buttons within a cell.

// (.cpp file)

    pGridBtnCell->SetupBtns(
        0,                  // zero-based index of image to draw
        DFC_BUTTON,         // type of frame control to draw e.g. DFC_BUTTON
        DFCS_BUTTONPUSH,    // like DrawFrameControl()'s nState  e.g. DFCS_BUTTONCHECK
        CGridBtnCellBase::CTL_ALIGN_CENTER,
                            // horizontal alignment of control image
        0,                  // fixed width of control or 0 for size-to-fit
        FALSE,              // T=btn is member of a radio group
        "Btn Text" );       // Text to insert centered in button; if NULL no text

How to Enable / Disable Buttons in Cells

Here's how to use MFC's ON_UPDATE_COMMAND_UI mechanism to "automatically" enable and disable buttons in the grid. This example is for grids that are placed in an MFC SDI or MDI window. CDialog-based grids can adapt code shown within the OnIdleUpdateCmdUI() function at the end of this section to "manually" enable and disable buttons.

Add an instance of the following to the header file of your CFormView derived class that hosts the grid:

// (.h file)

protected:
    unsigned long m_ulTicksLast; // for avoiding 100% CPU usage
    CGridCtrl m_Grid;            // the grid

    afx_msg LRESULT OnIdleUpdateCmdUI(WPARAM wParam, LPARAM);

Define macro for MFC's message passing scheme in the implementation file of your CFormView derived class:

// (.cpp file)

BEGIN_MESSAGE_MAP(CMyView, CFormView)
    //...
    ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
    //...
END_MESSAGE_MAP()

Implement button enabling / disabling in the general user interface updating routine that is called by MFC during idle-processing time:

// (.cpp file)

LRESULT CMyView::OnIdleUpdateCmdUI(WPARAM, LPARAM)
{
    // You'll use 100% cpu if you update the buttons continually
    unsigned long ulTicksNow = GetTickCount();
    if( ulTicksNow - m_ulTicksLast < 500 ) // <- nbr represents millisecs
        return 0L;
    m_ulTicksLast = ulTicksNow;

    if( !::IsWindow( m_Grid.m_hWnd ) )
        return 0L;  // gotta exist

    CCellRange VisibleRange;
    VisibleRange = m_Grid.GetVisibleNonFixedCellRange();
        // (yes, you could include fixed cells in your implementation)

    // for all visible cells that have a button
    int iRow, iCol;
    for( iRow=VisibleRange.GetMinRow(); iRow <= VisibleRange.GetMaxRow(); iRow++)
    {
        for( iCol=VisibleRange.GetMinCol(); iCol <= VisibleRange.GetMaxCol(); iCol++)
        {
            if( m_Grid.GetRowHeight( iRow) > 0
                && m_Grid.GetColumnWidth( iCol) > 0 )
            {           
                CGridBtnCellBase* pGridBtnCellBase
                            = (CGridBtnCellBase*)m_Grid.GetCell( iRow, iCol);
                ASSERT( pGridBtnCellBase != NULL);
                if( pGridBtnCellBase->IsKindOf( RUNTIME_CLASS(CGridBtnCellBase) ) )
                {
                    // here's where you add logic specific to your implementation...
                    BOOL bEnable = TRUE; // or = FALSE;
                    
                    // In this example, just assume that there's one button
                    //  in a cell
                    UINT uiState = pGridBtnCellBase->GetDrawCtlState( 0);
                    if( bEnable)
                    {
                        uiState &= ~DFCS_INACTIVE;
                    }
                    else
                    {
                        uiState |= DFCS_INACTIVE;
                    }
                    pGridBtnCellBase->SetDrawCtlState( 0, uiState);
                    m_Grid.RedrawCell( iRow, iCol);
                }
            }

        }
    }
    return 0L;
}

You May Place the Tree in a Fixed Grid Column

Want to try it? Change the following line defined at the top of GridTreeCtrlDemoDlg.cpp:

#define FIXED_COLUMN_COUNT      1

to:

#define FIXED_COLUMN_COUNT      3

Rebuild. The tree will be in a fixed column. You can increase the number to 4 or 5 to try out button cells as fixed, too.

Acknowledgements

Many thanks to Chris Maunder for his encouragement and suggestions.

Thanks to all who have submitted feedback and bug fixes. Special thanks to Michael A. Barnhart for his work on the insert / delete tree branches feature.

History

  • 17 Mar 2000 - Initial Release
  • 30 Jul 2000 - Updated to work with v2.20 of the grid control
  • 26 Apr 2001
    • Can dynamically insert and delete branches of the tree. Press these buttons:
      Insert/Delete Branches to try it. (Michael A. Barnhart)
    • The column with the tree may be a fixed column, now. Button cells may be in a fixed column, too.
    • If you clicked-down on a control embedded in a cell (e.g. push button, check box, etc.) then dragged you mouse away before releasing your mouse button, the control would remain in a state of limbo. Fixed. NOTE: This fix required a slight change in Chris' grid control code. (Search for "//." in GridCtrl.cpp to review the changes.) I've asked Chris to consider these changes for a future release.
    • Tree line drawing problem fixed. (Matthew Strawbridge; Hal DeVore)
    • Corrected tree image drawing when (nbr of fixed rows) != (nbr of fixed columns). (Andrey Babchenko)
    • Updated to work with v2.22 grid control.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ken Bertelson
Web Developer
United States United States
No Biography provided

Comments and Discussions

 
QuestionHow set tree in column number 0? PinmemberGAJERA3-Nov-10 0:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140905.1 | Last Updated 26 Apr 2001
Article Copyright 2000 by Ken Bertelson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid