Visit the Ultimate Grid main page for an overview and configuration guide to the Ultimate Grid library.
Contents
We've placed some answers here to a few of the most common questions folks have asked about when using various aspects of the Ultimate Grid. This page will probably grow as more people use the grid in different ways.
If you've come across an issue with the Ultimate Grid that needed a work-around or a bit of documentation that didn't quite explain all the ins and outs of a particular function call, please feel free to submit it for inclusion � you can be pretty sure it will be of help to someone else out there!
We've also included a small group of samples which are not part of the main sample projects download � these should form a FAQ directory in the Ultimate Grid directory when unzipped to the same location as the main downloads.
Q: In my application I need to perform a specific action when the grid loses focus, but the OnKillFocus notification is not always called. What can I do to correct this?
A: The Ultimate Grid will send the OnKillFocus notification every time it loses focus. This includes when a user starts to edit or shows a drop list. This means that if your grid does not have a drop list cell type and the user is not allowed to edit, it is safe to use the OnKillFocus notification and be sure that it will work.
But what if the user needs to edit cells? We've written a simple example that demonstrates the additions required to both the edit control and the drop list cell type in order for this to work properly.
See the sample project in the Faq\Focus Grid project included in the GridFaq_samples download.
Q: When opening a database created with Access 2000 through MFC DAO classes in Visual C++, you get the following error message: Unrecognized database format.
A: With the release of MS Access 2000 Microsoft has updated DAO to version 3.6, changes in the new version were very minor but big enough to prevent DAO 3.5 to open new files. In order to open the new files in MFC application we need to sync the current version of MFC - like so:
#ifdef _AFXDLL
AfxGetModuleState()->m_dwVersion = 0x0601;
#else
AfxGetStaticModuleState()->m_dwVersion = 0x0601;
#endif
It is best to use this code when the app is initialized. For more information please see article Q236991 in the MSDN.
Q: When a user is in edit mode and hits the TAB key, I need to finish editing and move to another cell.
A: This type of functionality can be achieved through use of the OnEditContinue
notification � returning FALSE from this notification prevents the grid from automatically activating the edit on the cell moved to:
int MyCug::OnEditContinue(int oldcol,long oldrow,int* newcol,long* newrow)
{
GotoCell( *newcol, *newrow );
return FALSE;
}
Q: When the user selects an item from a drop list cell, how can I examine the selected entry for validation?
A: You can easily get the text of an item selected by the user. The droplist celltype will send a message to the OnCellTypeNotify
notification indicating that the user has selected an item. At this point the param
passed in will point to a CString
which contains the selected text. The following code demonstrates how you can do this:
int MyCug::OnCellTypeNotify(long ID,int col,long row,long msg,long param)
{
int retval = TRUE;
if ( msg == UGCT_DROPLISTSELECT )
{
CString *tempStr;
tempStr = (CString*)param;
MessageBox( *tempStr );
}
return retval;
}
You can also reject the selection by returning FALSE.
Q: Is there a simple way to force the widths of the cells in the table heading and side headings to automatically size such that all the headings are visible?
A: Best way to get content of each cell to show fully is by using the BestFit
function. This function should be called after the grid window is created.
To get top headings to show up, just set the flag to UG_BESTFIT_TOPHEADINGS
, but to get the side header to adjust itself you must use '-1' as your starting column:
m_grid.BestFit( -1,
m_grid.GetNumberCols()-1,
m_grid.GetNumberRows()-1,
UG_BESTFIT_TOPHEADINGS );
Q: I am using DAO datasource and I seem to be getting a rather useless result from GetNumberRows(). When I call this function it tells me how many rows are visible on the screen rather than how many are in the grid. For example, a typical population would be twenty rows but it reports back the six that can be seen.
A: Yes - you are getting the rows that the grid knows about so far. If your tables are small, you can call the GetNumRowsComplete
method in the datasource class - if you do, you should probably SetNumberRows
to this value. Typically done after you open.
Q: I am using DAO datasource and I'm having trouble changing the state of cells after population. I can set the ReadOnly
state using SetColumnDefault
but if I try and set the state of individual cells later using GetCell
, SetReadOnly
, SetCell
it doesn't make any difference to it. I've tried using a RedrawAll
after setting all the ReadOnly
states I want but they still have no effect.
A: No, SetCell
when used with a datasource will typically not be able to store properties like color, readonly, etc. Here we use OnGetCell
to modify things as they are coming from the datasource.
For example, you would have to keep track of what cells are to be made editable then check in OnGetCell
whether that flag should be set.
If you're going to have to track this type of thing on a per cell basis, it might be best to default to read/write then do the checking in OnEditStart
, where you can cancel the edit.
Q: Whenever I call CUGCtrl::SetGridUsingDataSource()
(actually when AdjustComponents()
is called), the grid control takes the focus to itself. This interrupts our application's normal behavior. How can I get around this?
A: This problem can be solved by preventing the grid from updating itself.
This can be easily done by calling EnableWindow(FALSE)
on the grid. The following logic will do the trick:
- Disable grid window updates (
EnableUpate(FALSE) / EnableWindow(FALSE)
)
- Do some data operations (make sure that you do not call Redraw...)
- Enable updates
- Redraw All on the grid.
Q: I'm using a droplist and I want to fill the list dynamically. I handle the UGCT_DROPLISTSTART
message in which I set the list, but this code does not work:
GetCell(col,row,&cell);
cell.SetLabelText(DropList);
SetCell(col,row,&cell);
When I do this, however, the list is only changed the second time when I click.
A: The CUGDropListType
provides you with a pointer to CStringList
, which stores a list of items to be displayed by the droplist. This is done through param
passed into the OnCellTypeNotify
.
Knowing that, all you have to do is typecast the param
to a CStringList*
and you are free to make any change to the drop list items. The following code shows a simple example of this:
int MyCug::OnCellTypeNotify(long ID,int col,long row,long msg,long param)
{
CStringList *list;
list = (CStringList*)param;
if ( msg == UGCT_DROPLISTSTART )
{
list->AddTail( "My Element1" );
list->AddTail( "My Element2" );
list->AddTail( "My Element3" );
list->AddTail( "My Element4" );
}
}
Q: I am modifying a Client/Server (TCP/IP Internet) application to use the grid to display data. Our data sets are quite large so I only want to send a small subset of the data at a time for viewing to the Client (versus sending the entire data set). But, I still want the scroll bar to represent the entire data set on the server, instead of just what has been loaded into the grid. What would be the best way of doing this?
A: The scroll bar computes its relative size according to the number of rows CUGCtrl
thinks it has. This is not directly coupled with the number of rows in the datasource.
You can tell the grid it has 10,000 rows while connected to a datasource with 50 rows, as long as there are guards in place to deal with out of range calls to GetCell
in the datasource. (Which, in your case, will probably mean querying the server for a new range of records).
The best thing to do here here would be to derive a custom datasource to use with the grid and allow that to be your interface with the data on the server. The datasource will provide a good place to encapsulate the buffering you want for records.
Q: I want to move selected cells with drag-and-drop functionality build into the Ultimate Grid. I have tried to return 'DROPEFFECT_MOVE' from 'OnDragDrop' notification, but it does not seem to be working. Am I doing something wrong?
A: As it turns out the solution to the problem is quite simple and it does not require any modifications to the source code. To get the 'move' effect to work, all we have to do is clear data in selected cells. We have a chance to do this just before data is copied into the new location.
This sample code shows the technique:
DROPEFFECT MyCug::OnDragDrop(COleDataObject* pDataObject,int col,long row)
{
TRACE ( "OnDragDrop\n" );
int sCol;
long sRow;
CUGCell cell;
GetCell ( 0, 0, &cell );
m_GI->m_multiSelect->EnumFirstSelected( &sCol, &sRow );
do
{
cell.ClearAll();
SetCell ( sCol, sRow, &cell );
}
while ( m_GI->m_multiSelect->EnumNextSelected(
&sCol, &sRow ) == UG_SUCCESS );
return DROPEFFECT_COPY;
}
Q: When scrolling with scroll bars I want to keep the focus rect always in view, but I don't seem to get any notifications to inform me that the view was moved. What do I do?
A: The easiest way to solve this problem is by mapping the WM_SCROLL
message to your derived CUGCtrl
class and implementing the following function:
void CMyCug::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
long nOffset = GetCurrentRow() - GetTopRow();
CUGCtrl::OnVScroll( nSBCode, nPos, pScrollBar );
long yRow = GetCurrentRow();
long yTop = GetTopRow();
if ( yRow < yTop || yRow>= GetBottomRow() )
GotoRow( yTop + nOffset );
}
Q: I have looked through the grid but I haven't found a MoveRow()
function. I need to move a row/record in my grid to a new location, how do I do it?
A: One thing you could try is the following � note that it will only copy the text that is contained in the cells, it won't copy other cell properties:
CMyGrid::MoveRow( long lSourceRow, long lDestinationRow )
{
EnableUpdate( FALSE );
SetMultiSelectMode( TRUE );
SelectRange( 0, lSourceRow, GetNumberCols() - 1, lSourceRow );
CutSelected();
DeleteRow( lSourceRow );
InsertRow( lDestinationRow );
GotoCell( 0, lDestinationRow );
Paste();
EnableUpdate( TRUE );
RedrawAll();
}
Q: When I create a modal dialog (DoModal
function call) from either an OnLClicked
event, a button cell-type, an ellipsis cell-type, etc. The first mouse click on the dialog is captured by the grid not my dialog. What is going on?
A: This problem is caused by the fact that the Ultimate Grid sets capture to mouse events when user presses the mouse button, and releases it when user lets go. However, the mouse notifications are sent while the mouse capture is still active.
To solve our problem we have to release mouse capture just before the dialog becomes active, as in this sample:
void MyCug::OnLClicked( int col, long row, int updn, RECT *rect, POINT *point,
int processed )
{
if( !updn )
{
CMyDialog m_dialog;
ReleaseCapture ();
m_dialog.DoModal ();
}
}
Q: I am using a DAO datasource and I am trying to set a color to certain cells red, but I am not able to figure out how this can be done.
A: This problem is caused by the fact that the Ultimate Grid always relies on current datasource to provide it with all of the information about all of its cells. The problem with the DAO datasource (and other datasources that bind to a database) is that it is not able to store any information other than cell's value.
You can easily work around this problem with the use of the OnGetCell
notification. This notification is fired just before a cell is painted and can be used to set additional information (like back colors, fonts, cell types, etc.) to a cell. The information set here will not be stored back to the datasource, but it is a perfect place to customize how data is presented to the user.
For example, if you want to change background color of the cell (2,2) then your code might look like this:
void MyCug::OnGetCell(int col,long row,CUGCell *cell)
{
if ( col == 2 && row == 2 )
cell->SetBackColor ( RGB (0,255,0));
}
Note that at this point the datasource's GetCell
method has already been called, so OnGetCell
can be used for conditionals (like red text for negative values).
Q: Is it possible to use column swapping when my Grid has two top heading rows? When I swap a col (row -1 col 1) and there is a cell over this col (row -2 col 1 and col 2) that is joined with another cell (row -2 col 2). When I now put the cell (row -1 col 1) and move it to to col 5, then the cell (-2 1) is moved also. But over the Cell (-1 2) is nothing (no Cell, before there was the joined cell). Is is possible to change the behavior of the Grid so that the two cols (1, 2) under the joined cell (-2,1 ; -2 ,2) are moved to the new Position?
A: This problem is caused by the fact that the grid ignores all top headings, except for the first, when swapping columns. And because you have the joins in second top heading everything gets mixed up.
To solve this problem you will have to make some simple modifications to the source code.
First you will have to create virtual member functions for both CUGCtrl
and you derived grid class. The function should be declared and used as:
virtual BOOL OnColSwapped ( int startCol, int endCol );
BOOL UGCtrl::OnColSwapped(int startCol, int endCol)
{
return TRUE;
}
BOOL MyCug::OnColSwapped(int startCol, int endCol)=
{
TRACE ( "OnColSwapped\n" );
JoinCells ( 0, -2, GetNumberCols () -1, -2 );
return TRUE;
}
And finally we activate the notification that we have created:
void CUGTopHdg::OnLButtonUp(UINT nFlags, CPoint point)
{
.... ... ...
if(m_ctrl->OnCanColSwap(m_swapStartCol,end) != FALSE)
{
m_ctrl->MoveColPosition(m_swapStartCol,m_swapEndCol,TRUE);
m_ctrl->OnColSwapped ( m_swapStartCol, m_swapEndCol );
.... ... ...
Q: If I want to pre-select multiple cells in the code for the users, what function that I can use? There is no SetSelect()
in class CUGMultiSelect
!
A: The solution to this problem is quite simple, all you have to do is call CUGMultiSelect::StartBlock
function from OnSetUp
of your CUGCtrl
derived class (or any other place that you might want to select cells from):
m_GI->m_multiSelect->StartBlock ( col, row );
The parameters specify the column and row of the cell to be selected, you can call it multiple times if you want more cells to be selected.
Q: I noticed that when I am doing a FitToWindow()
, with all my fields represented (0, maxFieldNum) that as I expand, it resizes fine, but when I shrink it, the last field does not get resized and therefore everything else shrinks in proportion. So if you go back and forth getting larger and smaller, eventually everything else disappears except the last column.
A: This problem is caused by the fact that the column width is stored as an integer value.
The following is code for a FitToWindow function that has been added to a CUGCtrl
derived class (MyCug
). As you can see this function is not using the current col width but a value stored in the array of integers (of original values). This way the fit to window calculation will always use the same values and will not be affected by the previously sized window:
CArray m_originalCols;
int MyCug::FitToWindow(int startCol, int endCol)
{
RECT rect;
int screenwidth, columnwidths, loop, newwidth;
float ratio;
if(startCol <= m_GI->m_numberCols)
return 1;
m_CUGGrid->GetClientRect(&rect);
screenwidth = rect.right;
columnwidths = 0;
for( loop = startCol; loop <= endCol; loop++ )
{
columnwidths += m_originalCols[loop];
}
ratio = ( (float)screenwidth ) / ( (float)columnwidths );
for( loop = startCol; loop < endCol; loop++) {
newwidth = (int)( (float)m_originalCols[loop] * ratio );
SetColWidth( loop,newwidth );
screenwidth -= newwidth;
}
SetColWidth( endCol, screenwidth );
Update();
return UG_SUCCESS;
}
int MyCug::StoreOriginalCols()
{
m_originalCols.SetSize ( GetNumberCols (), 0 );
for ( int col = 0; col < GetNumberCols(); col ++ )
{
m_originalCols.SetAt ( col, GetColWidth( col ));
}
return UG_SUCCESS;
}
Q: When I am editing a cell in the Ultimate Grid, and I right-mouse click, a menu pops up that has the options 'Undo/Cut/Copy/Paste...'. I do NOT want this to pop up. How can I disable it?
A: Due to the fact that the edit control in the Ultimate Grid is derived from the CEdit
class, every function and notification is available for our use.
The notification that allows us to disable or change the Clipboard popup menu in our edit control is:
void CUGMaskedEdit::OnContextMenu(CWnd* pWnd, CPoint point)
Now if we just want to get rid of the default context menu, then we can just leave the function empty (no code). However, if we want to display the same menu as in the grid (in non edit mode) than we will need to implement following code:
void CUGMaskedEdit::OnContextMenu(CWnd* pWnd, CPoint point)
{
CMenu *tempMenu = m_ctrl->GetPopupMenu ();
tempMenu->TrackPopupMenu( TPM_LEFTALIGN, point.x, point.y, pWnd );
}
Q: I have called StartEdit() from OnLClicked() and as soon as I am done editing it replaces the text I just entered with the stuff that was there before. Help me!
A: OnLClicked()
gets called twice by the grid. Once in response to the windows message WM_LBUTTONDOWN
and once in response to the windows message WM_LBUTTONUP
. When the left mouse button has been depressed, OnLClicked()
gets called with updn = 1. When the button is released OnLClicked()
gets called with updn = 0. So in OnLClicked()
, if you do not check the state of this variable and you call StartEdit()
, you will be starting editing twice. The normal procedure is to start editing when the button is released.
void CRandoGrid::OnLClicked( int col, long row, int updn, RECT* rect,
POINT* point, int processed )
{
if( !updn )
StartEdit();
}
Q: I can change the background color of the cells in my grid, but the area not covered by cells in the grid window is always the standard window color or white. How do I change the color of this region?
A: The Ultimate Grid provides a notification where you can change the color to be used in this region. This notification is called OnGetDefBackColor()
. If you do not override this notification in your own derived grid class, the base class (CUGCtrl
) will always return the standard windows color based on your system colors. Return a COLORREF
for the color you want this region to be painted with:
COLORREF CTestGrid::OnGetDefBackColor( int nSection )
{
if( m_bGridHasFocus == TRUE )
return RGB( 255, 255, 255 );
else
return GetSysColor( COLOR_BTNFACE );
}
Q: When I start editing a cell of the grid, the edit control becomes really big (larger than the underlying cell), and as I type it continues to get bigger and bigger. What is going on?
A: The default edit control that comes with the Ultimate Grid (derived from the class CUGEdit
) contains the ability to automatically resize itself in response to the user's input. It also has the ability to size itself to fit the initial font that is in the cell that is being currently edited. This auto size capability can be turned off at your discretion:
( ( CUGEdit* )GetEditClass() )->SetAutoSize( FALSE );
You would probably want to do this globally for your entire grid based app and would do it from OnSetup()
.
Q: The data in one of my columns is numbers, but when I go and sort it, the grid sorts it as text. How do I get the grid to sort it as numeric data?
A: When it comes time to sort the contents of a column, the grid will call the notification OnSortEvaluate()
and pass in the two cell objects it is currently trying to sort. The default implementation of OnSortEvaluate()
checks the datatype of the objects involved and sorts them appropriately. You can ensure that your numeric data is sorted as numeric data and not as string if you set the cell data type of the cells in that column to numeric data.
CUGCell cell;
GetColDefault( 4, &cell );
cell.SetDataType( UGCELLDATA_NUMBER );
SetColDefault( 4, &cell );
You would most likely do the above from OnSetup()
. Once done, OnSortEvaluate
will sort the data appropriately:
int CTestGrid::OnSortEvaluate( CUGCell* cell1, CUGCell* cell2, int flags )
{
if( flags & UG_SORT_DESCENDING )
{
CUGCell *ptr = cell1;
cell1 = cell2;
cell2 = ptr;
}
switch(cell1->GetDataType())
{
case UGCELLDATA_STRING:
if( NULL == cell1->GetText( ) && NULL == cell2->GetText( ) )
return 0;
if( NULL == cell1->GetText( ) )
return 1;
if( NULL == cell2->GetText( ) )
return -1;
return _tcscmp( cell1->GetText( ), cell2->GetText( ));
case UGCELLDATA_NUMBER:
case UGCELLDATA_BOOL:
case UGCELLDATA_CURRENCY:
double n1 = cell1->GetNumber( );
double n2 = cell2->GetNumber( );
if( n1 < n2 )
return -1;
if( n1 > n2 )
return 1;
return 0;
}
if( NULL == cell1->GetText( ) )
return 1;
if( NULL == cell2->GetText( ) )
return -1;
return _tcscmp( cell1->GetText( ), cell2->GetText( ) );
}
One problem you could encounter is if your data is being entered/modified by the user from keyboard entry. As soon as editing finishes taking place on a cell, the text the user entered into the edit control is copied back to the cell of the grid by calling QuickSetText()
. Now by default QuickSetText()
not only changes the text in the cell specified but also sets the cell datatype of that cell to UGCELLDATA_STRING
. But we want the data to be numeric or it won't sort properly � to ensure that the data of our numeric column is numeric data (even after the user has edited it), we would do something like this:
void CTestGrid::OnSetCell( int col, long row, CUGCell* cell )
{
if( col == 4 )
cell->SetDataType( UGCELLDATA_NUMBER );
}
Q: I have written a routine that goes through and appends a bunch of rows, adds bitmaps to cells, resets the width and height of all components in my grid and changes all of the background colors to a pink & green checkerboard, every time I call this function the grid flickers up a storm as it repaints itself in response to all of these changes. How do I stop the grid from painting itself?
A: There are two different functions you can use to turn off the grid's internal ability to repaint itself:
EnableUpdate( FALSE );
SetPaintMode( FALSE );
To turn on the ability to redraw you would call the same function again, this time with the parameter = TRUE. After calling this function, the grid will not repaint itself at all, and manual calls to RedrawAll()
will also cease to function.
Q: I do not want one or both of the headings to show up (top or side heading). How do I get rid of them?
A: To hide one of the headings you simply set it's width or height to 0 (zero). For the top headings it would be like this:
SetTH_Height( 0 );
For the side heading it would be like this:
SetSH_Width( 0 );
You would probably call either of these from OnSetup()
of your grid class.
NOTE: If either the side heading or top heading is not present (has a width or height of zero), then the corner button will not displayed.
Q: I created a font and set it as the default font of the grid using SetDefFont()
, but I do not see my font being used. What is going on?
A: The most likely reason is that the font is going out of scope before the grid can use it to draw the text in the cells. When you create a GDI object for use with the Ultimate Grid, you should make that object a member of your derived grid class. Then it will always be available to the grid. In your destructor for your grid class, you'll destroy the object.
void CTestGrid::OnSetup()
{
CDC * pDC;
pDC = GetDC();
nHeight = -MulDiv( 10, GetDeviceCaps( pDC->m_hDC, LOGPIXELSY), 72 );
ReleaseDC( pDC );
m_font.CreateFont( nHeight, 0, 0, 0, 400, 0, 0, 0, 0, 0,0, 0, 0, _T(
"Verdana" ) );
SetDefFont( &m_font );
}
The above code snippet creates a font (which is a member of CTestGrid
), scales it to the correct size for the screen and sets it as the default font to be used in the grid.
Q: How come when I set a mask to a cell using QuickSetMask()
or CUGCell::SetMask()
and then go and edit the cell, the mask does not do anything?
A: The most likely reason is the you are not using the Ultimate Grid masked edit control. The masked edit control (CUGMaskedEdit
) is a separate class from the Ultimate Grid's standard edit control (CUGEdit
). You can find a copy of the masked edit control in your \addons directory. Using the masked edit control is a simple process. First create an instance of CUGMaskedEdit
and make it a member of your derived grid calls (MyCug
for example). Creation of the masked edit control is a two stage process, after the constructor, you should call Create()
on the control (again here from OnSetup()
):
CRect cRect;
m_maskedEditCtrl.Create( WS_CHILD | WS_VISIBLE, cRect, this, 13523523 );
Now that the control is created we need to tell the grid to use the control when we edit. This can be done from OnEditStart
:
int CTestGrid::OnEditStart( int col, long row, CWnd** edit )
{
if( col == 4 )
*edit = &m_maskedEditCtrl;
return TRUE;
}
In the above example, we only want to use the masked edit control in the fifth column of our grid, otherwise we will use the standard masked edit control. If we wanted this edit to be the default for all cells, we could call SetNewEditClass()
.
Q: I have put the Ultimate Grid into an MFC extension .DLL and it does not work properly or blows up on me in certain places. What gives?
A: If you use the AFX_EXT_CLASS
macro to export the classes you need from the Ultimate Grid, everything should work fine. One thing that should be noted, the Ultimate Grid souce code will conditionally compile in code if OLE has been enabled (you have included afxole.h in your project). When an MFC extension .DLL is created, the standard stdafx.h has afxole.h included so all of the code (drag and drop stuff) that needs OLE present will be compiled into the .DLL. When you go and create a project that uses the MFC extension .DLL you will also have to include afxole.h in your stdafx.h header or the code will not match up and you will encounter serious problems.
Q: I am using a datasource (DAO, ODBC, custom) and I have called all sorts of QuickSet... functions to change things in the cells of my grid and I do not see these changes. What did I do wrong?
A: When you use an external datasource, the Ultimate Grid expects all of the data it needs for the cells to be coming from that datasource. If there are certain things that are not specified in the datasource that you want to display in the grid (fonts, joined cells, colors, etc), you have to set them manually yourself from OnGetCell()
. OnGetCell()
is called in response to either yourself or the grid (internally) calling GetCellIndirect()
. When it comes time to paint the cells of the grid in the current window, the grid internally does a GetCellIndirect()
to get the information it needs to paint each of those cells. Even if there is no information in the datasource for the cells we want to modify, or we want to change that default information, we are given a chance in OnGetCell()
to intercept the data before it gets displayed.
void CRandoGrid::OnGetCell( int col, long row, CUGCell* cell )
{
if( col >= 0 && row >= 0 )
cell->SetNumber( (col+1) * (row+1) );
}
The above code will fill all the cells of the grid (not the headers) with a multiplication table, irrelevant of what the datasource is holding (e.g. a DAO table).
Since OnGetCell()
already provides a pointer to the cell object, we don't need to do a GetCell()
call (in fact doing so could lead to infinite recursive calls!). Likewise you should be wary of using QuickSet
... members from OnGetCell()
. As OnGetCell()
gets called a lot, it is not a good idea to put a lot of code here and one should never put a loop of any sort in OnGetCell()
, or you will take a huge performance hit.
Q: How do I get the DropList index value of the item selected?
A: For this we will have to add new notification to the droplist box that will give us the index. Since the drop list box used in the grid is derived from MFC's List box it is easy to get the extended functionality.
First we will have to define a constant for the new notification that we are going to create. You can do this in the header file for the DropList class:
#define UGCT_DROPLISTSELECTEX 102
Now, lets find the CUGLstBox::Select
function. Here we are actually going to pass the index value to the OnCellTypeNotify
:
void CUGLstBox::Select()
{
CUGCell cell;
CString string;
int col = *m_col;
long row = *m_row;
m_selected = TRUE;
GetText(GetCurSel(),string);
m_ctrl->OnCellTypeNotify(m_cellTypeId,col,row, UGCT_DROPLISTSELECTEX,
(long)GetCurSel());
And finally in the OnCellTypeNotify
override we can handle the notification and display it in a message box:
else if (msg == UGCT_DROPLISTSELECTEX)
{
string = new CString;
string->Format ("%d",param);
MessageBox (*string);
}
Q: For my application, I want a fixed number of columns, and I want to get rid of the horizontal scroll bar. How do I do this?
A: The easiest way to hide the scroll bars is by setting their height/width to 0.
m_grid.SetHS_Height ( 0 );
m_grid.SetVS_Width ( 0 );
Q: Is there a member function to delete (clear) all the rows, except the headers, in the Ultimate Grid control?
A: No. You can either iterate through a for loop and call DeleteRow()
to delete all of the rows or in one go, you can do the following:
SetNumberCols(0);
SetNumberRows(0);
GetDataSource(0)->Empty();
(this assumes you're not bound to a database, you're just using the grid's memory manager). The only drawback to this method is that it will get rid of everything (including the headings and all setup information you've applied to the grid).
Use the above, but first create some sort of GridSetup()
function. There set up the number of column, rows, the headings information, global parameters (like SetHighlightRow()
, SetMultiSelectMode()
, SetVScrollMode()
, EnabelCellOverLap()
, etc). Then when you delete everything, you can quickly get everything set up again when you call your GridSetup()
function
Q: In my application I need to present users with a popup menu that contains sub menus, how can I do this?
A: The Ultimate Grid allows you to get pointer to its popup menu. You can then use that CMenu
object to create complex menus. The following code sample demonstrates how you can create sub-menu options:
int MyCug::OnMenuStart(int col,long row,int section)
{
EmptyMenu();
CMenu *gridMenu = GetPopupMenu();
CMenu subMenu;
if ( section == UG_GRID )
{
gridMenu->AppendMenu( 0, 1001, "1st option" );
gridMenu->AppendMenu( 0, 1002, "2nd option" );
gridMenu->AppendMenu( MF_SEPARATOR );
subMenu.CreatePopupMenu();
subMenu.AppendMenu( MF_CHECKED, 1011, "3rd option" );
subMenu.AppendMenu( 0, 1012, "4th option" );
subMenu.AppendMenu( MF_GRAYED, 1013, "5th option" );
gridMenu->AppendMenu( MF_POPUP, (UINT)subMenu.GetSafeHmenu(),
"More options" );
}
return TRUE;
}
This sample uses the OnMenuStart
notification to add the menu options, which shows the potential for flexible menus. However, if in your application grid will have the same popup menu at all times you can setup the menu from OnSetup
.
Q: I found that the GetCellFromPoint function does not work for heading cells, is there a way can I correct this?
A: By design the GetCellFromPoint
function will not work for cells that are on headings.
We've written a customized version of the GetCellFromPoint
function to provide this type of functionality, called FindCellFromPoint
so there is no confusion.
See the example project in the Faq\GetCellFromPoint directory of the UltimateGridFaq_samples download.
Q: In my application I need to limit number of characters my end users are allowed to type.
A: The easiest way to accomplish this is not through use of mask edit control but through OnEditVerify notification.
For example, to limit user's input to 50 characters you can use following code:
int MyCug::OnEditVerify(int col, long row,CWnd *edit,UINT *vcKey)
{
if ( m_editCtrl->GetWindowTextLength() >= 50 )
{
return FALSE;
}
return TRUE;
}
Q: I am having problems tabbing between dialog items when the grid is on the dialog, I am not able to tab out of the grid.
A: The grid control has been designed in such a way to capture all input keys, this was done to provide maximum flexibility to our users. To accomplish this we have handled the OnGetDlgCode
message in the CUGGrid
class and returned DLGC_WANTALLKEYS
.
UINT CUGGrid::OnGetDlgCode()
{
if ( IsChild( GetFocus()))
return DLGC_WANTALLKEYS|DLGC_WANTARROWS;
else
return CWnd::OnGetDlgCode()|DLGC_WANTARROWS|DLGC_WANTALLKEYS;
}
The easiest way to correct this situation and allow your users to tab in and out of the grid (without modifications to our source code) is with the use of the OnKeyDown
notification in your CUGCtrl
derived class. Like so:
void MyCug::OnKeyDown(UINT *vcKey,BOOL processed)
{
if ( *vcKey == VK_TAB && GetKeyState( VK_SHIFT ) < 0 )
{
((CDialog*)GetParent())->PrevDlgCtrl();
}
else if ( *vcKey == VK_TAB )
{
((CDialog*)GetParent())->NextDlgCtrl();
}
}
Initial CodeProject release October 2007.