Click here to Skip to main content
15,884,885 members
Articles / Desktop Programming / MFC

CGridCtrl 1.5

Rate me:
Please Sign up or sign in to vote.
3.95/5 (19 votes)
17 Jun 20042 min read 132.5K   6.4K   41  
This article presents a tiny grid control derived from the standard list control
//-------------------------------------------------------------------
//
//  $Id: CGridCtrl.h,v 1.10 2004/06/16 23:49:26 miezoo Exp $
//  Author: Marius Negrutiu (18.05.2004 21:13:10)
//	mailto://negrutiu@as.ro
//
//-------------------------------------------------------------------

#pragma once
#ifndef CGRIDCTRL_H
#define CGRIDCTRL_H

#define GRID_VERSION				0x0105	// version 1.5

#define DEFAULT_EDIT_LIMIT			100
#define DEFAULT_BUFFER_LENGTH		255
#define	DEFAULT_TIMER_INCSEARCH		2500	// incremental search reset period (default 2.5 sec)

#define This						const_cast<CGridCtrl*>(this)
#define ROW							(int)mDraw.cd->nmcd.dwItemSpec
#define COL							mDraw.cd->iSubItem

//
//	enum CellType
//
enum CellType
{
	CELL_REGULAR = 0,		// regular cell (text (and image) only)
	CELL_CUSTOMEDITOR,		// editable cell (the OnCustomCellClick callback is called)
	CELL_EDITBOX,			// editable cell (edit box)
	CELL_COMBOBOX,			// editable cell (drop-down-list combo box)
	CELL_EDITCOMBOBOX,		// editable cell (drop-down combo box)
	CELL_CHECKBOXON,		// editable cell (check box ON)
	CELL_CHECKBOXOFF,		// editable cell (check box OFF)
	CELL_SEPARATOR			// not editable cell
};

//
//	enum FontStyle
//
enum FontStyle
{
	FONT_BOLD		= 1,
	FONT_ITALIC		= 1 << 1,
	FONT_UNDERLINE	= 1 << 2,
	FONT_STRIKEOUT	= 1 << 3
};

//
//	class CGridCtrl
//
class CGridCtrl: public CListCtrl
{
	DECLARE_DYNAMIC(CGridCtrl)

public:
	CGridCtrl();
	virtual ~CGridCtrl();

/*
	PURPOSE:
		Scrolls the grid control so that the specified column gets visible

	ARGUMENTS:
		Col			Column index

	RETURN:
		The amount of pixels necessary to scroll the
		grid in order to make this column visible. If the
		return value is 0, the column was already visible.
*/
	int		EnsureColVisible(IN int Col);
	
/*
	PURPOSE:
		Determines the cell that contains the supplied point

	ARGUMENTS:
		Pt			The point in client coordinates
		Row			The hit cell row
		Col			The hit cell column
		Rect		The cell rectangle

	RETURN:
		TRUE if a cell is hit. FALSE == whitespace.
*/
BOOL	CellHitTest(IN	const CPoint& Pt,
					OUT	int &Row,
					OUT	int	&Col,
					OUT	RECT *Rect = NULL OPTIONAL);

/*
	PURPOSE:
		Retrieve a cell bounding rectangle.

	ARGUMENTS:
		Self explanatory

	RETURN:
		TRUE for success
*/
	bool	GetCellRect(	IN  int Row,
							IN  int Col,
							OUT RECT& Rect);

/*
	PURPOSE:
		Ensure the supplied rectangle does not exceed the
		grid control's client area. (The rectangle is intersected
		with the grid client rect)

	ARGUMENTS:
		Self explanatory
*/
	void	ValidateCellRect(IN OUT RECT* rect);

/*
	PURPOSE:
		Get the index of the focused item (this information is read
		from the parent list control)

	RETURN:
		The index or -1 if no item has the focus
*/
	int		GetFocused() const;

/*
	PURPOSE:
		Get the focused cell (this information is read from the cached
		coordinates)
*/
	inline void GetFocusedCell(OUT int& Row, OUT int& Col) { Row = mRow, Col = mCol; };

/*
	PURPOSE:
		Select the specified cell and ensure it is visible.

	ARGUMENTS:
		Row			...
		Col			...
		DoRedraw	If true, the cell that was previously selected
					is redrawn for clearing the focus rectangle. Also,
					the new selected cell is redrawn for painting
					the focus rectangle.
	RETURN:
		TRUE if success
*/
	bool	SelectCell(IN int Row, IN int Col, IN bool DoRedraw = false);

/*
	PURPOSE:
		Deselect all (selected) items, select and focus the current cell
*/
	void	DeselectAll();

/*
	PURPOSE:
		Enter edit mode

	ARGUMENTS:
		Row
		Col
		CellRect		If NULL, GetCellRect(...) is called automatically

	RETURN:
		TRUE if success and the inplace editor was created
*/
	bool	EditCell(	IN int Row,
						IN int Col,
						IN const RECT* CellRect = NULL OPTIONAL);

/*
	PURPOSE:
		GetColumnCount
*/
	inline	int GetColumnCount() const { return This->GetHeaderCtrl()->GetItemCount(); };

/*
	PURPOSE:
		HasGridLines
*/
	inline	bool HasGridLines() const { return ((This->GetExtendedStyle() & LVS_EX_GRIDLINES) != 0); };

/*
	PURPOSE:
		Redraws the specified row. If CheckVisibility == true,
		the row is redrawn only if its visible in the current view.
*/
	void RedrawItem(IN int Row, IN bool CheckVisibility = true);

/*
	PURPOSE:
		Get/set the in-place editors text limit. This limit prevents
		large string typing. The editors that uses this information
		are EDITBOX-es and EDITCOMBOBOX-es.
*/
	inline	UINT16	GetInplaceEditLimit() const { return mEditLen; };
	void	SetInplaceEditLimit(UINT16 Limit) { mEditLen = Limit; };

/*
	PURPOSE:
		CGridCtrl has an internal buffer used for any string operation.
		This buffer MUST be large enough to carry any string displayed by
		the grid control.
		The following routines handle its size.
*/
	inline	UINT16	GetStringBufferLen() const { return mBufferLen; };
	void	SetStringBufferLen(UINT16 Len);

/*
	PURPOSE:
		Enable/disable grid's incremental search support.
*/
	inline	bool GetIncrementalSearch() const { return mIncSearch; };
	inline	void SetInctementalSearch(bool Enable = true) { mIncSearch = Enable; };

/*
	PURPOSE:
		Controls the time after an incremental search is reset.
*/
	inline	UINT16 GetIncrementalSearchTimer() const { return mIncSearchTimer; };
	inline	void   SetIncrementalSearchTimer(IN UINT16 msecs) { mIncSearchTimer = msecs; };

/*
	PURPOSE:
		Set/reset drawing colors
*/
	inline	void SetColors(	COLORREF ColorFrame,
							COLORREF ColorNormalFill,
							COLORREF ColorSelFill,
							COLORREF ColorBtnFill,
							COLORREF ColorNormalText,
							COLORREF ColorSelText) { mDraw.SetColors(ColorFrame, ColorNormalFill, ColorSelFill, ColorBtnFill, ColorNormalText, ColorSelText); CacheVisuals(); };
	inline  void SetColorsToDefault() { mDraw.ResetColors(); CacheVisuals(); };

protected:
	DECLARE_MESSAGE_MAP()

/*
	PURPOSE:
		Cache colors and generate bitmaps.
*/
	virtual void CacheVisuals();

/*
	PURPOSE:
		Window message handlers
*/
	afx_msg virtual void	OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg virtual BOOL	OnLClick(NMHDR* hdr, LRESULT* res);
	afx_msg virtual void	OnLButtonDblClk(UINT nFlags, CPoint point);
	afx_msg virtual void	OnRButtonDown(UINT nFlags, CPoint point);
	afx_msg virtual BOOL	OnRClick(NMHDR* hdr, LRESULT* res);
	afx_msg virtual void	OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
	afx_msg virtual void	OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
	afx_msg virtual void	OnSetFocus(CWnd* pOldWnd);
	afx_msg virtual void	OnKillFocus(CWnd* pNewWnd);
	afx_msg virtual BOOL	OnEraseBkgnd(CDC *dc);
	afx_msg virtual void	OnSysColorChange();
	afx_msg virtual LRESULT	OnSetImageList(WPARAM wp, LPARAM lp);

	afx_msg virtual void	OnDestroy();
	afx_msg virtual void	OnTimer(UINT_PTR nIDEvent);

	virtual void	PreSubclassWindow(void);
	virtual BOOL	OnCommand(IN WPARAM wp, IN LPARAM lp);

/*
	PURPOSE:
		Callbacks for mouse events

	NOTE:
		Make sure you supercall the default implementation!

	RETURN:
		"OnCellClick" must return TRUE if any inplace editor was created.
*/
	virtual bool	OnCellLBtnDown(		IN int Row,
										IN int Col,
										IN const RECT *CellRect,
										IN const CPoint& Point);

	virtual void	OnCellLBtnUp(		IN int Row,
										IN int Col,
										IN const RECT *CellRect,
										IN const CPoint& Point);

	virtual void	OnCellRBtnDown(		IN int Row,
										IN int Col,
										IN const RECT *CellRect,
										IN const CPoint& Point);

	virtual void	OnCellRBtnUp(		IN int Row,
										IN int Col,
										IN const RECT *CellRect,
										IN const CPoint& Point);

	virtual void	OnCellDblClick(		IN int Row,
										IN int Col,
										IN const RECT* CellRect);

/*
	PURPOSE:
		Called when a check-box cell was clicked

	NOTE:
		Make sure you supercall the default implementation!
*/
	virtual void	OnCheckBoxClick(int Row, int Col);

/*
	PURPOSE:
		Called when a custom-edit cell was clicked
*/
	virtual void	OnCustomCellClick(int Row, int Col);

/*
	PURPOSE:
		Called when an inplace combo box is about to be displayed. The routine should
		provide the items for the combo box to be populated. It also may specify an icon
		for each item. In this case it must set 'HasImages' to TRUE, and fill 'Images'
*/
	virtual void	OnGetComboBoxItems(	IN  int Row,
										IN  int Col,
										OUT bool& HasImages,
										OUT CStringList& Items,
										OUT CArray<int, int>& Images);

/*
	PURPOSE:
		Called when an inplace editor is about to be destroyed. If the return value
		is FALSE, the editor will not be destroyed and the edit operation will continue.
		This is the right place to perform input validation.
*/
	virtual bool	OnEndInPlaceEdit(	IN int Row,
										IN int Col,
										IN bool Canceled,
										IN const TCHAR* Text,
										IN int Image);

/*
	PURPOSE:
		Called when an ASCII key is pressed.

	NOTE:
		Make sure you supercall the default implementation!
*/
	virtual void	OnIncrementalSearch(IN TCHAR ch);

/*
	PURPOSE:
		Called for each cell for determining its type
*/
	virtual CellType	OnGetCellType(IN int Row, IN int Col);

/*
	PURPOSE:
		Called for each cell in order to overwrite the global colors.
		Additional information can be found in mDraw.
*/
	virtual bool	OnGetCellColors(	IN  int Row,
										IN  int Col,
										OUT COLORREF& TextColor,
										OUT COLORREF& BkColor,
										OUT UINT8& TextStyle) const;

/*
	PURPOSE:
		Create in-place controls.
*/
	virtual bool	CreateInPlaceEdit(IN int Row, IN int Col, IN const RECT *CellRect);
	virtual bool	CreateInPlaceCombo(IN int Row, IN int Col, IN const RECT *CellRect, IN bool Editable);

/*
	PURPOSE:
		The DrawXxxCell routines handle cell painting.
*/
	virtual void __fastcall DrawRegularCell();
	virtual void __fastcall DrawEditBoxCell();
	virtual void __fastcall DrawComboBoxCell();
	virtual void __fastcall DrawCheckBoxCell();
	virtual void __fastcall DrawCustomCell();
	virtual void __fastcall DrawSeparatorCell();

protected:
	bool		mDestroying;

	int			mEditRow, mEditCol;	// the cell that is currently beeing edited
	int			mRow, mCol;			// the cell that has the focus

	UINT16		mEditLen;			// edit box text limit
	TCHAR*		mBuffer;			// global buffer for handling strings
	UINT16		mBufferLen;

	bool		mIncSearch;
	CString		mIncSearchString;
	HWND		mIncSearchTip;
	UINT16		mIncSearchTimer;

protected:
	struct TransientCustomDraw
	{
		NMLVCUSTOMDRAW*		cd;
		bool				Focused;
		bool				Selected;
		UINT8				Justify;
		RECT				Rect;
		const TCHAR*		CellText;
		CellType			Type;
		int					Icon;

		bool				GridLines;

		HBITMAP				ComboBmp;
		HDC					ComboDC;
		HBITMAP				CustomBmp;
		HDC					CustomDC;
		HBITMAP				CheckOnBmp;
		HDC					CheckOnDC;
		HBITMAP				CheckOffBmp;
		HDC					CheckOffDC;

		bool				UseDefaultColors;
		HBRUSH				BrushFrame;
		COLORREF			ColorFrame;
		HBRUSH				BrushNormalFill;
		COLORREF			ColorNormalFill;
		HBRUSH				BrushSelectedFill;
		COLORREF			ColorSelectedFill;
		HBRUSH				BrushButtonFill;
		COLORREF			ColorText;
		COLORREF			ColorSelText;

		HFONT				CustomFont;
		UINT8				CustomFontStyle;

		HBITMAP				DefaultStockBmp;
		bool				Initialized;

		void KillObjects()
		{
			if (!Initialized) return;

			::DeleteObject(::SelectObject(ComboDC, DefaultStockBmp));
			::DeleteDC(ComboDC);

			::DeleteObject(::SelectObject(CustomDC, DefaultStockBmp));
			::DeleteDC(CustomDC);

			::DeleteObject(::SelectObject(CheckOnDC, DefaultStockBmp));
			::DeleteDC(CheckOnDC);

			::DeleteObject(::SelectObject(CheckOffDC, DefaultStockBmp));
			::DeleteDC(CheckOffDC);

			Initialized = false;
		}

		void KillBrushes()
		{
			::DeleteObject(BrushFrame);
			::DeleteObject(BrushNormalFill);
			::DeleteObject(BrushSelectedFill);
			::DeleteObject(BrushButtonFill);
		}

		void SetColors(	COLORREF ClFrame,
						COLORREF ClNormalFill,
						COLORREF ClSelFill,
						COLORREF ClBtnFill,
						COLORREF ClText,
						COLORREF ClSelText)
		{
			// destroy previous brushes
			if (!UseDefaultColors)
				KillBrushes();

			// new brushes
			ColorFrame			= ClFrame;
			BrushFrame			= ::CreateSolidBrush(ClFrame);
			ColorNormalFill		= ClNormalFill;
			BrushNormalFill		= ::CreateSolidBrush(ClNormalFill);
			ColorSelectedFill	= ClSelFill;
			BrushSelectedFill	= ::CreateSolidBrush(ClSelFill);
			BrushButtonFill		= ::CreateSolidBrush(ClBtnFill);
			ColorText			= ClText;
			ColorSelText		= ClSelText;

			// mark custom colors flag
			UseDefaultColors = false;
		}

		void ResetColors()
		{
			// destroy previous brushes
			if (!UseDefaultColors)
				KillBrushes();

			// set to defaults
			ColorFrame = ::GetSysColor(COLOR_BTNSHADOW);
			BrushFrame = ::GetSysColorBrush(COLOR_BTNSHADOW);
			ColorNormalFill = ::GetSysColor(COLOR_WINDOW);
			BrushNormalFill = ::GetSysColorBrush(COLOR_WINDOW);
			ColorSelectedFill = ::GetSysColor(COLOR_HIGHLIGHT);
			BrushSelectedFill = ::GetSysColorBrush(COLOR_HIGHLIGHT);
			BrushButtonFill = ::GetSysColorBrush(COLOR_BTNFACE);
			ColorText = ::GetSysColor(COLOR_WINDOWTEXT);
			ColorSelText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
		}

		TransientCustomDraw(): Initialized(false), UseDefaultColors(true), CustomFont(NULL), CustomFontStyle(0) { ResetColors(); };
		~TransientCustomDraw()
		{
			if (!UseDefaultColors)
				KillBrushes();
			KillObjects();
			if (CustomFont)
				::DeleteObject(CustomFont);
		}
	};

	// transient drawing info (valid beetween OnCustomDraw and DrawXxxCell(..) only!)
	mutable TransientCustomDraw mDraw;

private:
/*
	PURPOSE:
		Handles NM_CUSTOMDRAW
*/
	BOOL		OnCustomDraw(IN NMHDR* Header, OUT LRESULT* Result);

/*
	PURPOSE:
		Generates a new font based on the current one, and caches it
		within mDraw

	ARGUMENTS:
		Style		Is a combination of 'enum FontStyle' values
*/
	void		GenerateFont(IN UINT8 Style);

private:
	// window procs for in-place editors
	static WNDPROC				mOldChildProc, mOldComboEditProc, mOldComboListProc;
	static HWND					mComboLBox;
	static bool					mComboLBoxClicked;
	static INT_PTR CALLBACK		EditBoxProc(HWND, UINT, WPARAM, LPARAM);
	static INT_PTR CALLBACK		ComboBoxProc(HWND, UINT, WPARAM, LPARAM);
	static INT_PTR CALLBACK		ComboEditProc(HWND, UINT, WPARAM, LPARAM);
	static INT_PTR CALLBACK		ComboListProc(HWND, UINT, WPARAM, LPARAM);
};

#endif //CGRIDCTRL_H

//-------------------------------------------------------------------
//	HISTORY
//-------------------------------------------------------------------
//
//	$Log: CGridCtrl.h,v $
//	Revision 1.10  2004/06/16 23:49:26  miezoo
//	- vc6 compilable
//	
//	Revision 1.9  2004/06/16 18:20:58  miezoo
//	- mouse related notifications
//	- new events for button-ups
//	
//	Revision 1.8  2004/06/16 18:04:12  miezoo
//	- AFX message handlers made virtual
//	
//	Revision 1.7  2004/06/14 08:10:01  miezoo
//	- version [1.5]
//	
//	Revision 1.6  2004/06/09 17:00:51  miezoo
//	- version [1.4]
//	- separator cells
//	
//	Revision 1.5  2004/06/08 19:44:27  miezoo
//	- version [1.3]
//	- on-demand cell type
//	- colors and font styles
//	- USE_DEFAULT_ERASEBKGND vanished
//	
//	Revision 1.4  2004/06/06 23:13:03  miezoo
//	- version [1.2]
//	- comments
//	- incremental search timer exposed
//	
//	Revision 1.3  2004/06/06 12:58:28  miezoo
//	- version 1.1
//	- private members exposed
//	
//	Revision 1.2  2004/06/06 12:07:20  miezoo
//	- Version added
//	
//	Revision 1.1  2004/06/06 11:49:06  miezoo
//	- Initial revision
//	
//-------------------------------------------------------------------

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Romania Romania
- C/C++ Programming
- Windows Device Driver programming (DDK)
- Low-level networking programmer (firewalls, routers, sniffers...)
- AutoCAD ObjectARX/DBX programming
- ADT Object Modeling Framework (OMF) programmer

Comments and Discussions