Click here to Skip to main content
15,879,613 members
Articles / Web Development / HTML
Article

Tile Grid Control

Rate me:
Please Sign up or sign in to vote.
4.83/5 (22 votes)
5 Mar 2024CPOL8 min read 33.5K   2.8K   36   13
An MFC CWnd derived grid of user definable tiles
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.

Image 1

Image 2

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.

C++
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.

C++
class CChildView : public CTileGrid
    {
        ...
C++
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
      return -1;

    // create a view to occupy the client area of the frame
    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.

C++
class CMyAppView : public CTileGridView
{
    DECLARE_DYNCREATE(CMyAppView)
    ...
C++
CMyAppView::CMyAppView()
    : CTileGridView(RUNTIME_CLASS(CTileGrid))
    {
        ...

Set the TGS_* tile grid control styles from your view class' OnCreate member function.

C++
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.

C++
void CMyAppView::OnInitialUpdate()
{
    CTileGridView::OnInitialUpdate();

    CTileGrid& TheGrid = GetTileGrid();     // Get the tile grid
    TheGrid.SetTileSize(50, 50, false);     // all tiles are 50 by 50 pixels
    TheGrid.SetTileSpacing(2);              // 2 pixel space between tiles

    for (int x = 0; x < 50; ++x)            // add 50 tiles
    {
        CMyTile pTile = new CMyTile;        // CMyTile derived from CTileBase
        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
    • Posted on CodeProject
  • 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

License

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


Written By
President
Canada Canada
Father of two, brother of two, child of two.
Spouse to one, uncle to many, friend to lots.
Farmer, carpenter, mechanic, electrician, but definitely not a plumber.
Likes walks with the wife, board games, card games, travel, and camping in the summer.
High school graduate, college drop-out.
Hobby programmer who knows C++ with MFC and the STL.
Has dabbled with BASIC, Pascal, Fortran, COBOL, C#, SQL, ASM, and HTML.
Realized long ago that programming is fun when there is nobody pressuring you with schedules and timelines.

Comments and Discussions

 
Praisethank you! Pin
SDK201912-Mar-24 15:55
SDK201912-Mar-24 15:55 
QuestionDemo Pin
dseverns510-Mar-24 8:48
professionaldseverns510-Mar-24 8:48 
AnswerRe: Demo Pin
PJ Arends10-Mar-24 20:41
professionalPJ Arends10-Mar-24 20:41 
GeneralRe: Demo Pin
dseverns511-Mar-24 2:10
professionaldseverns511-Mar-24 2:10 
GeneralRe: Demo Pin
PJ Arends11-Mar-24 4:41
professionalPJ Arends11-Mar-24 4:41 
QuestionNo download sources Pin
_Flaviu6-Mar-24 21:41
_Flaviu6-Mar-24 21:41 
AnswerRe: No download sources Pin
PJ Arends7-Mar-24 6:55
professionalPJ Arends7-Mar-24 6:55 
AnswerRe: No download sources Pin
PJ Arends7-Mar-24 10:13
professionalPJ Arends7-Mar-24 10:13 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA6-Mar-24 14:56
professionalȘtefan-Mihai MOGA6-Mar-24 14:56 
QuestionThe CtileGrid derivation loses the friend attribute Pin
cqyczj21-May-21 16:09
cqyczj21-May-21 16:09 
GeneralDone a similar CWnd control to display a grid of images Pin
Shao Voon Wong3-Jul-19 0:29
mvaShao Voon Wong3-Jul-19 0:29 
PraiseGood work! Pin
YLAROYE24-Jun-19 1:51
YLAROYE24-Jun-19 1:51 
GeneralMy vote of 5 Pin
Farhad Reza16-Nov-16 8:43
Farhad Reza16-Nov-16 8:43 

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

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