

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:
CGridCtrl m_Grid;
CTreeColumn m_TreeColumn;
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.
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.
m_Grid.SetColumnCount( 4);
m_Grid.SetFixedColumnCount( 1);
m_TreeColumn.TreeSetup( &m_Grid,
2,
sizeof( ucPatternAry),
1,
ucPatternAry,
TRUE,
TRUE);
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:
CGridCtrl m_Grid;
CBtnDataBase m_BtnDataBase;
Associate the grid with the cell property holder in the implementation file of the CDialog or CView.
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.
CGridBtnCell GridCellCopy;
GridCellCopy.SetBtnDataBase( &m_BtnDataBase);
CGridCellBase* pCurrCell = m_Grid.GetCell( row, col);
if (pCurrCell)
GridCellCopy = *pCurrCell;
After the grid's window created, replace default cells with button cells as required. The following code is called in CDialog::OnInitDialog()
.
m_Grid.SetCellType( row, col, RUNTIME_CLASS(CGridBtnCell) );
CGridBtnCell* pGridBtnCell = (CGridBtnCell*)m_Grid.GetCell( row, col);
pGridBtnCell->SetBtnDataBase( &m_BtnDataBase);
Setup controls within new button cell. The example implementation allows up to 4 buttons within a cell.
pGridBtnCell->SetupBtns(
0,
DFC_BUTTON,
DFCS_BUTTONPUSH,
CGridBtnCellBase::CTL_ALIGN_CENTER,
0,
FALSE,
"Btn 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:
protected:
unsigned long m_ulTicksLast;
CGridCtrl m_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:
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:
LRESULT CMyView::OnIdleUpdateCmdUI(WPARAM, LPARAM)
{
unsigned long ulTicksNow = GetTickCount();
if( ulTicksNow - m_ulTicksLast < 500 )
return 0L;
m_ulTicksLast = ulTicksNow;
if( !::IsWindow( m_Grid.m_hWnd ) )
return 0L;
CCellRange VisibleRange;
VisibleRange = m_Grid.GetVisibleNonFixedCellRange();
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) ) )
{
BOOL bEnable = TRUE;
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:
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.