This may be a Grid Control but, call me Array






4.85/5 (15 votes)
Jul 16, 2004
6 min read

84039

3992
A control that implements an array of items
Introduction
Frequently, different kind of information must be presented to the user in two dimensional form. Often, to do this, we can use ListView or TreeView controls, but sometimes this is not appropriate, or it is hard to code or cannot be used in their native form. Besides, we wish keep difference between elements and display them in appropriate form (e.g., one element show a color table, another show a text or image, and so on). Therefore, We need to have a flexible mode to operate. Control presented here implements a such array of items (like array or matrix in mathematics) which are freely drawn by user. An array is, for example, an image or a color table or whatever information that can be presented in two dimensions (third dimension can be implemented using TabControl as show in demo program). Control assist user in the items arrangement, in the resize of control, in the scroll and in the notify of events inherent the control itself. All items of the array have same size (i.e. width and height), that the control will use to arranging them into rectangle of its client area. Dimensions (i.e. columns and rows) and item size can be changed by the user in any instant, columns or rows can be fixed or dynamic (control will compute them at any change). Since each item will be drawn by user, any kind of things can be drawn on it. To do this, control pass to the user a Device Context (DC) specific for the item that is drawing. User paint the given DC and the control will render it to its window at the appropriated position (based on current scroll position and items arrangement).
Control creation
Array control is implemented by class CArrayCtrl
which was inherit from CWnd class. Constructor method initialize members variables and destroyer method is not used. The control's window class has name "ArrayControlClass" and will be register at the first call of create method. Control is created using method Create
which synopsis is:
BOOL CArrayCtrl::Create(DWORD dwStyle, const CRect& rect, CWnd* pParent, UINT nID, const CSize& itemsize);where
dwStyle specifies the control style attributes WS_CHILD //create a child window (this style will be enforced by control) //(control enforce WS_HSCROLL and WS_VSCROLL too) WS_VISIBLE //window is visible WS_BORDER //window has a border ACS_TOOLTIP //control support item's tooltip ACS_TTBALLOON //tooltip style is ballon form (default is the standard rectangular form) ACS_BALLOONTIP //combines two previously defined styles (ACS_TOOLTIP | ACS_TTBALLOON) ACS_GRIDLINES //gridlines are required ACS_CLIENTEDGE //specifies thar window has 3D look, that is, a border with sunken edge //(this style is a redefinition of corresponding Windows style) ACS_USERDATA //user will store data (an LPARAM type) on each item //(by default, control don't store any information) rect //the size and position of the window, //in client coordinate of pParent pParent //the parent window nID //the id of the child window itemsize //the size of the item (cx is the width and cy is the height)
The following code show how to create such control
CArrayCtrl* pArrayCtrl; // CArrayCtrl object pointer declaration CSize ItemSize(32,64); CRect rControl(8,8,200,100); pArrayCtrl = new CArrayCtrl; pArrayCtrl->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ACS_BALLOONTIP | ACS_GRIDLINES, rControl, pParentWnd, ID_ARRAY, ItemSize);After this, window is displayed on screen but none item is visible. You have two mode to specify the items to the control. You can use method
SetItemsCount
(int nItems) that force the control to start item-paint process, or method AddItem(int nItem, LPARAM dwData)
that insert a new item into the control based to nItem
value. It can be an item index (such as 5) and the control will insert new item after that, or the special item ACAI_TOP
and ACAI_BOTTOM
(default), meaning of which is obvious. Since AddItem
method will redraw control's window each time item is added, we must suspend redrawing process until all items are been added. To do this you will use the SetRedraw(BOOL bDraw)
method to stop the drawing process (bDraw=FALSE
) and then restart it (bDraw=TRUE
). This code snippet show it: pArrayCtrl->SetRedraw(FALSE);
while( ... )
{
... prepare data item ...
pArrayCtrl->AddItem(dwData);
}
pArrayCtrl->SetRedraw(TRUE);
If you previously know the items count, you can use SetItemsCount
method in this way: ... control is already been created ... pArrayCtrl->SetRedraw(FALSE); pArrayCtrl->SetItemsCount(100); for(i=0; i<100; i++) { ... prepare data item ... pArrayCtrl->SetItemData(i, dwData); } pArrayCtrl->SetRedraw(TRUE);Naturally, if you don't need to have userdata associated to the items, you can call above methods without call SetRedraw.
Methods
There are several methods grouped by category and it will be listed with a brief description. For more specific information on code utilization you should look source code implementation and demo.
General UINT GetCtrlId (); //return the child window id void SetCtrlData (LPARAM dwData); // set a global user data to the control LPARAM GetCtrlData (); //return the global user data associated to the control void SetToolTip (DWORD ttstyle); //enable items tooltip (ttstyle is a TOOLTIP style) void SetCursor (HCURSOR hCursor); //set the window cursor with a specific one void SetRedraw (BOOL bResize); //disable or enable items drawing process void Activate (BOOL bFlag); //hide (bFlag=FALSE) or show (bFlag=TRUE) the control's window
Items void SetItemCount (int nItems); //set the number of items in a control int GetItemCount (); //retrieve the current items count DWORD GetItemState (int nItem); //retrieve the item state //ACIS_NORMAL none of the follow (item is normal) //ACIS_SELECTED item is been selected //ACIS_VISIBLE item is visible //(totally or partially) in the current view //ACIS_HIDDEN item is hidden (out of current view) int GetRowIndex (int nItem); //return the item row index (in the current arrangement) int GetRowIndex (); //same as above but for selected item int GetColumnIndex (int nItem); //return the item column index (in the current arrangement) int GetColumnIndex (); //same as above but for selected item void AddItem (int nItem, LPARAM dwData); //insert an item into control void AddItem (LPARAM dwData) //add an item to the bottom void DrawItem (int nItem); //force an item redraw void RemoveItem (int nItem); //delete an item from the control (items will be rearranged) void RemoveAllItems (); //delete all items from the control LPARAM SetItemData (int nItem, LPARAM dwData); // associate user data to an item LPARAM GetItemData (int nItem); //retrieve the user data from an item
Sizing and dimensioning void SetItemSize (int width, int height); //set the item size void SetItemSize (CSize* pSize); //set the item size void SetDimension (int nCount, int obj); // set a dimension to be fixed, obj is the dimension object //ACDT_COLUMNS columns must be fixed to given number //ACDT_ROWS rows must be fixed to given number void SetDimension (int nCount); //set the current dimension to be fixed int GetDimension (int* dimtype); //get the dimension count and type int GetWidth (); //get the control's window width int GetHeight (); // get the control's window height void GetItemSize (CSize* pSize); //get the item size int GetItemWidth (); //get the item width int GetItemHeight (); //get the item height void SetColumns (int nCol); //set the columns to be fixed to given number void SetRows (int nRow); //set the rows to be fixed to given number int GetColumns (); //get the number of columns int GetRows (); //get the number of rows
Colouring void SetBkColor (COLORREF crBack); //set the background color of the control (and the item) void SetGridColor (COLORREF crGrid); //set the gridlines color void SetColors (COLORREF crBack, COLORREF crGrid); //set the color of background and the gridlines (together) COLORREF GetBkColor (); // get the background color COLORREF GetGridColor (); //get the gridlines color void SetTipColor (COLORREF crBkg, COLORREF crTxt); //set the tooltip background and foreground colors
Selection and Scrolling void SelectItem (int nItem); //set the item state to selected void ActivateItem (int nItem); //activate an item (send LBUTTONDBLCLK message to control) void ActivateItem (); //same as above but for selected item int GetSelectedItem (); //get the item that was been selected int GetHitItem (); //get the item under the mouse pointer void SetScrollSize (int nSBVers, int nSBObj, int nSize); //set the scrolling amount value //nSBVers SB_VERT //SB_HORZ //nSBObj ACSO_LINE //ACSO_PAGE int GetScrollSize (int nSBVers, int nSBObj); //get the amount of the scroll component void Scroll (int nSBVers, UINT nSBCode); //send a scroll event to control (amount is default) //nSBCode SB_TOP // SB_BOTTOM // SB_LEFT // SB_RIGHT // SB_PAGEUP // SB_PAGEDOWN // SB_LINEUP // SB_LINEDOWN void Scroll (int nSBVers, UINT nSBCode, UINT nPos); //send a scroll event to control with amount specified void VertScroll (UINT nSBCode); //send a vertical scroll event to control (amount is default) void VertScroll (UINT nSBCode, UINT nPos); //send a vertical scroll event to control with amount specified void HorzScroll (UINT nSBCode); //send an horizontal scroll event to control (amount is default) void HorzScroll (UINT nSBCode, UINT nPos); //send an horizontal scroll event to control with amount specified void EnsureVisible (int nItem); //ensure that item is visible and scroll control to make this BOOL IsVisible (int nItem); //verify if item is on current view
Grid Lines void SetGridLines (BOOL bActive); //activate or deactivate gridlines BOOL GetGridLines (); //get gridlines activation status void SetGridThickness (int nThickness); //set the thickness of the gridlines int GetGridThickness (); //get the thickness of the gridlines
Notification process
Control send to parent window some notification messages using the WM_NOTIFY
form. It is sent by control to its parent window when an event has occurred or the control requires some information. Events which notification is required are listed below:
- mouse action over control
- item draw
- item deletion
- tooltip event
ON_NOTIFY(wNotifyCode, CtrlId, memberFunction) or ON_NOTIFY_RANGE(wNotifyCode, CtrlIdFirst, CtrlIdLast, memberFunction)Your member functions must be declared with the following prototypes:
afx_msg void memberFunction(NMHDR* pNotifyStruct, LRESULT* result); or afx_msg void memberFunction(UINT CtrlId, NMHDR* pNotifyStruct, LRESULT* result); where the parameters are: CtrlId the child identifier of the control that sent the notification. pNotifyStruct a pointer to the notification structure, as described above. result a pointer to the result code you’ll set before you return.
WM_NOTIFY
messages contain the ID of the control sending the message in wParam
and a pointer to an NMHDR
structure in lParam
. This structure is followed by a related event structure that has an NMHDR
structure as its first member. Note that since the NMHDR
member is first, a pointer to this structure can be used as either a pointer to an NMHDR
or as a pointer to the structure depending on what type of event is, and you must cast it to proper structure.
For mouse activation events control send an ACITEMACTIVATE
which have this declaration:
typedef struct tagACITEMACTIVATE { NMHDR hdr; // standard header CArrayCtrl* pControl; // control object pointer int nItem; // item activate DWORD dwState; // item state int nRow; // row (relative to current arrangement) int nColumn; // column (relative to current arrangement) CPoint ptAction; // mouse pointer location at event UINT nKeyFlags; // modifier key that were pressed LPARAM dwParam; // user data } ACITEMACTIVATE, *LPACITEMACTIVATE;The field
code
of the NMHDR
structure will report the event id: ACN_CLICK // User clicked left mouse button in the control ACN_RCLICK // User clicked right mouse button in the control ACN_DBCLICK // User double-clicked left mouse button in the controlFor item draw and deletion events control send an
ACITEMINFO
which have this declaration: typedef struct tagACITEMINFO { NMHDR hdr; // standard header CArrayCtrl* pControl; // control pointer int nItem; // item to be draw DWORD dwState; // item state int nRow; // row (relative to current arrangement) int nColumn; // column (relative to current arrangement) CDC* pDC; // DC to be draw the item int nWidth; // width of the given DC int nHeight; // height of the given DC LPARAM dwParam; // user data } ACITEMINFO, *LPACITEMINFO;The field
code
of the NMHDR structure will report the event id: ACN_DRAWITEM // Item must be drawn ACN_DELETEITEM // Item is been deleted
On item deletion only pControl
, nItem
and dwParam
fields are filled with meaningful information. For tooltip events control send an ACTOOLTIPINFO
which have this declaration:
typedef struct tagACTOOLTIPINFO { NMHDR hdr; // standard header CArrayCtrl* pControl; // control pointer int nItem; // item for which tooltip text is required DWORD dwState; // item state int nRow; // row (relative to current arrangement) int nColumn; // column (relative to current arrangement) LPTSTR lpszTipText; // address of need text buffer (max size is MAX_PATH) LPARAM dwParam; // user data } ACTOOLTIPINFO, *LPACTOOLTIPINFO;For the time being, the field
code
of the NMHDR structure will only report the event id: ACN_NEEDTIPTEXT Tooltip require a text
The demo program
Demo program creates four controls to show any kind of operation performed with Array control. zbr> First and second control are CArrayCtrl
which show images, icons and text loaded by selecting a folder. Images will be shown as thumbnail, while icons and text will be shown as they are. A CxImage class, by Davide Pizzolato (see his article), is used to load images. I have used the related LIB built on my computer and I have included the original header file. The right button of the mouse show two menus: one if pointer is over an item and another if pointer is over a control but not over an item (know as item-menu and control-menu) that enable user to choose various operation.
Third control show an image selected from above controls and splashed on it. Selected icon will be shown using IconEdit program (.ico file extension must be associated to this via desktop) and text using Notepad. Other stuff can be chosen. There isn't menu associated to this control. Fourth control is a TabCtrl used for contrasting color test. It has ten items that specify the color Brightness value, and each item has an array control which items specify the Saturation and Hue values. This is a 3-dimensional control. By select an item, a new window will be displayed. In this window you can see two rectangle coloured with contrasting color of the background color: left rectangle is computed by alucardx algorithm (see article on general section) and right rectangle is computed by my own. At the side of each there are related color information. Two small icons rating the goodness of algorithms.
Enhancements and improvements
This is a list of open issues about CArrayCtrl
control:
- Make internal use of TabControl to implement 3-D array (this isn't a real limitation)
- Use Vector template to manage user data
- Sending
WM_NOTIFY
for mouse movements - Make item management more complex (with more to use defaults)
- Implement items hot-tracking
- Make each item a mini-window with a own life
- Link each item to a thread to perform asynchronous operation
History
- This is the version 1 modification 0. I don't believe that all bugs were discovered.