This article introduces a CWnd derived MFC control for presenting dynamic grids of tiles, inspired by Windows Explorer's tile view, outlining its structure, usage, and integration within MFC applications for versatile display of data.
Introduction
This article presents a CWnd
derived MFC control for presenting data as a dynamic grid of tiles. I use the term dynamic because the number of tiles in a row or column depends on the size of the window and the size of the tiles and the layout will change as the tile grid's window size changes. The tiles are layed out as a list where they fill one row or column at a time, rather than the traditional grid where tiles are layed out in designated rows and columns.
The idea for the control came from the tile view in Windows Explorer. Originally, I tried to twist the icon view of a CListCtrl
to do what I wanted, but it was way too limited. First off, I could not get the icons big enough for what I wanted, and it was also impossible to get the icons to respond to user input in any intelligent way.
I tried asking my old friend Google to see if she knew of any MFC controls that would do what I wanted, but there was no love found there either. So the only option left was to write a control from scratch that did exactly what I wanted. Presented here is the result.
The Tile Grid Control
The Tile Grid Control is made up of six MFC classes that work together to make the control do what needs to be done. The classes are listed below. I will not list the entire API of the classes here in this article as there are a total of one hundred and sixty one individual functions between the six classes. Listing them here would make this article way too long and make looking up the API way too unwieldy. I have instead included a compiled help file that lists all of the public
and protected
member functions.
The classes that make up the tile grid control are:
CTileGrid
- The
CTileGrid
class is the main window class for the tile grid control. It is derived directly from CWnd
. This class can be used directly as is, or more likely will serve as the base class for your own custom grid control. The CTileGrid
class is responsible for maintaining the layout of all the tiles and for handling all windows messages related to the users interactions with the control.
CTileBase
- The
CTileBase
class is the base class from which you would derive your own tile classes. CTileBase
is derived from CObject
and is declared as an abstract base
class. It has a protected
constructor so you cannot directly construct a CTileBase
class object . CTileBase::OnDraw
is declared as a pure virtual
function so in order to use this class, you have to override OnDraw
in your derived class to draw the tiles onto the grid. - Any user input that happens in the tile grid gets forwarded to the
CTileBase
tile that the input was directed towards. So the tiles can respond to any mouse actions, and if the tile has the keyboard input focus, it can respond to any non-system key presses.
CWindowScroller
- The
CWindowScroller
class is used to provide the middle mouse button panning control to the tile grid.
CTileGridTip
- The
CTileGridTip
class is a simple CWnd
derived class that acts as a single line tooltip window for the CTileGrid
control. CTileGridTip
can be used as is or it can be used as a base class for a more elaborate tooltip control.
CTileGridView
- The
CTileGridView
class was written so the tile grid could be used in an SDI or MDI MFC Doc/View model application. CTileGridView
is derived from CView
. CTileGridView
has a protected
constructor so it can only be used as a base class for your own view class. It is a simple class that contains the CTileGrid
control in its client area.
TileBaseReturn
- The
TileBaseReturn
class is used as the return value from the CTileGrid::GetTileReturn
function. Many of the CTileGrid
functions will call into CTileBase
functions and the only way for derived classes to get the data returned from the CTileBase
functions from within your CTileGrid
derived class is to use CTileGrid::GetTileReturn
.
Using the Tile Grid Control
The Tile Grid control can be used in many different applications. It can be used as a control on a dialog template, as the main window in a simple application, or as a view in a multiple or single document interface program. No matter which way it is used, the first step to use the control in your own application is to add all the files to your project. The files are located in the TileGrid folder in the downloads.
The files needed are:
- TileGrid.h, TileGrid.cpp: These files define the
CTileGrid
and TileBaseReturn
classes. - TileBase.h, TileBase.cpp: These files define the
CTileBase
class. - TileGridTip.h, TileGridTip.cpp: These files define the
CTileGridTip
class. - WindowScroller.h, WindowScroller.cpp: These files define the
CWindowScroller
class. - TileGridView.h, TileGridView.cpp: These files define the
CTileGridView
class. The use of these files is optional as they are only needed if the tile grid is to be used as a view in a Doc/View application.
In a Dialog Template
To add the tile grid to a dialog template, simply add it as a custom control and specify the required TGS_*
creation styles in the Style
property. Add a CTileGrid
or derived member variable to the dialog
class and in your DoDataExchange
function, add a DDX_Control
function to associate your member variable with the custom control.
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
DDX_Control(pDX, IDC_CUSTOM1, m_TileGrid);
}
That is it, now the tile grid can be accessed though the CTileGrid
member variable.
See the TileGridDialog
demo application included in the demo download for an example.
As the Main Window
To use the tile grid as the main window of an application without Doc/View support, it is simply a matter of using the new app creation wizard to create an SDI app and make sure you clear the Document/View support checkbox. Then, it is simply a matter of making sure your CChildView
class is derived from CTileGrid
or a class derived from it. Then in CMainFrame::OnCreate
, place a call to CChildView::Create
to create the tile grid, specifying the TGS_*
tile grid creation styles that are required.
class CChildView : public CTileGrid
{
...
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndView.Create(
AFX_WS_DEFAULT_VIEW | TGS_RESIZABLE | TGS_MULTISELECT | TGS_PANNING,
CRect(0, 0, 0, 0),
this,
AFX_IDW_PANE_FIRST,
NULL))
{
TRACE0("Failed to create view window\n");
return -1;
}
See the TileGridView
demo application included in the demo download for an example.
In a Doc/View Application
To use the CTileGrid
control in a CView
derived window that can be used in an SDI/MDI application, you have to derive your view class from CTileGridView
. You then pass the RUNTIME_CLASS
of the CTileGrid
or derived class to the CTileGridView
class constructor.
class CMyAppView : public CTileGridView
{
DECLARE_DYNCREATE(CMyAppView)
...
CMyAppView::CMyAppView()
: CTileGridView(RUNTIME_CLASS(CTileGrid))
{
...
Set the TGS_*
tile grid control styles from your view class' OnCreate
member function.
int CMyAppView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
lpCreateStruct->style |= TGS_HORIZONTAL;
return CTileGridView::OnCreate(lpCreateStruct);
}
See the TileGridDV
demo application included in the demo download for an example.
Adding Tiles
Once the tile grid window has been created, you can add the tiles you want to use. Each tile that you use has to be of a class derived from CTileBase
. Every class derived from CTileBase
has to, at a minimum, define its own OnDraw
method that will draw the tile onto the tile grid.
The tiles are added to the grid using the CTileGrid::AddTile
function. You will also want to set the size of the tiles using the CTileGrid::SetTileSize
function. If you do not set the size, all the tiles will default to their minimum 2 by 2 pixel size. You may also want to space the tiles apart by using the CTileGrid::SetTileSpacing
function. The tiles will be tight together otherwise.
void CMyAppView::OnInitialUpdate()
{
CTileGridView::OnInitialUpdate();
CTileGrid& TheGrid = GetTileGrid(); TheGrid.SetTileSize(50, 50, false); TheGrid.SetTileSpacing(2);
for (int x = 0; x < 50; ++x) {
CMyTile pTile = new CMyTile; TheGrid.AddTile(pTile, true);
}
}
Creating Derived Classes
Class | Inheritable | Runtime information macro |
CTileBase | Yes - Required | DYNAMIC |
CTileGrid | Yes - Optional | DYNCREATE |
CTileGridTip | Yes - Optional | DYNCREATE |
CTileGridView | Yes - Required | DYNCREATE |
CWindowScroller | No | - |
TileBaseReturn | No | - |
In order to use the CTileBase
and CTileGridView
classes, one has to derive your own classes from them. CTileGrid
and CTileGridTip
can be used as-is or they can serve as a base class for your own derived classes. The CWindowScroller
and TileBaseReturn
classes are helper classes and should not be inherited from.
CTileBase
has been created using the DECLARE_DYNAMIC
and IMPLEMENT_DYNAMIC
macros. While it is not necessary for any derived classes to use these same macros, it is probably not a bad idea to use them.
If you inherit a class from CTileGrid
, CTileGridView
or CTileGridTip
, be sure to use the DECLARE_DYNCREATE
and IMPLEMENT_DYNCREATE
macros as these classes are created dynamically. In order to use a derived tooltip class as the tooltip control for the tile grid, you have to call CTileGrid::SetTooltip
, before CTileGrid::Create
is called, specifying the runtime class of your derived tooltip class. If you are using a derived tooltip in a CTileGridView
derived view, then you would pass the runtime class of the tooltip as the second parameter to the CTileGridView
constructor.
History
- 10th May, 2016
- 23rd June, 2019
- Added to the
CTileGrid
class:
AddTile(CRuntimeClass *)
GetMinMaxTileSize
GetTGStyle
GetTileSizeDelta
IsResizingTiles
IsTileFullyVisible
ModifyTGStyle
OnEnable
OnPrintClient
RemoveSelectedTiles
SetMinMaxTileSize
SetTGStyle
- Changed in the
CTileGrid
class:
GetTileFromPoint
- Added the hit test parameter IsStyleSet
- Renamed to IsTGStyleSet
OnGetDlgCode
- Added the WPARAM
and LPARAM
parameters
- Changed the
TGS_*
styles
- Removed
TGS_RESIZABLE
- Added
TGS_RESIZABLEVERT
, TGS_RESIZABLEHORZ
, TGS_RESIZABLEKEYS
, and TGS_RESIZABLEMOUSE
- Added
CTileGrid
parent window notifications - Removed the
CTGSortFunction
and CTGFindFunction
types - Added to the
CTileBase
class:
GetFont
GetSize
IsFullyVisible
OnAddedToGrid
OnGetDlgCode
OnLButtonClick
OnMButtonClick
OnRButtonClick
OnRemovedFromGrid
OnSize
OnXButtonClick
- 6th March, 2024
- Added to the
CTileGrid
class:
OnTimer
SetTileTimer
KillTileTimer
OnTileMsg
- Added to the
CTileBase
class:
OnTimer
SetTimer
KillTimer
OnTileMsg
OnVisibility