

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.