Board index » cppbuilder » StringGrid - repainting problem

StringGrid - repainting problem


2005-08-04 07:10:55 PM
cppbuilder5
I have a stringgrid on the form. DrawCell event handler paints fixed columns
and rows of selected area with different color compared to unselected. When
selection is done using mouse everything works well since grid is repainted
using MouseMove event. Problem occurs when moving around using array-keys.
Currently I repaint grid using KeyUp event:
void __fastcall TForm1::StringGrid1KeyUp(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if ((Key == VK_UP) || (Key == VK_DOWN) || (Key == VK_RIGHT) || Key ==
VK_LEFT) StringGrid1->Invalidate();
StringGrid1->Options>>goEditing;
}
But this fires event after key is released (as expected). This makes fixed
column/row jump after cell has already changed and if array key is pressed
continuously effect is even more clear.
How (and where) should I repaint grid so that there is no delay ?
Pauli
 
 

Re:StringGrid - repainting problem

Now I found the right event handler. It is of course SelectCell.
void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol,
int ARow, bool &CanSelect)
{
StringGrid1->Invalidate();
}
Pauli
 

Re:StringGrid - repainting problem

"Pauli" < XXXX@XXXXX.COM >wrote:
Quote

Now I found the right event handler. It is of course SelectCell.

void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol,
int ARow, bool &CanSelect)
{
StringGrid1->Invalidate();
}

I'm quite sure that you don't need to repaint the grid every
time a cell is selected. Please explain exactly how you want
the grid to behave.
~ JD
 

{smallsort}

Re:StringGrid - repainting problem

Quote
I'm quite sure that you don't need to repaint the grid every
time a cell is selected. Please explain exactly how you want
the grid to behave.

~ JD

Default color of fixed rows and cols (Col=0, Row=0 ) is clBtnFace as usual.
Selected cells and Col=0 and Row=0 are clSkyBlue.
When the user changes selection using mouse or array keys old and new
selections shoud be repained. I suppose there is better way to do that than
repaint hole grid but even this way it works fast enougth so that user does
not show it.
If you can tell a better way I'm happy to hear it.
Pauli
 

Re:StringGrid - repainting problem

"Pauli" < XXXX@XXXXX.COM >wrote:
Quote

[...] When the user changes selection using mouse or array
keys old and new selections shoud be repained. [...]
The default behavior already handles that so your OnDrawCell
event must not be crafted correctly. It should look something
like:
//-------------------------------------------------------------
void __fastcall TForm1::StringGrid1DrawCell(int ACol, int ARow, const TRect &Rect, TGridDrawState State)
{
TStringGrid *pGrid = static_cast<TStringGrid*>( Sender );
TCanvas *pCanvas = pGrid->Canvas;
// pCanvas->Font->Color = pGrid->Font->Color;
if( State.Contains(gdFixed) )
{
if( ACol == 0 && ARow == 0 ) pCanvas->Brush->Color = clSkyBlue;
else pCanvas->Brush->Color = pGrid->FixedColor;
}
else
{
if( State.Contains(gdSelected) ) pCanvas->Brush->Color = clSkyBlue;
else pCanvas->Brush->Color = pGrid->Color;
}
pCanvas->FillRect( Rect );
::DrawText( pCanvas->Handle, pGrid->Cells[ACol][ARow].c_str(), -1, &Rect, DT_SINGLELINE | DT_VCENTER | DT_LEFT );
}
//-------------------------------------------------------------
To AutoSize the columns in your grid:
// do something to populate the grid
AutoSizeColumns( StringGrid1 );
//-------------------------------------------------------------
void __fastcall TForm1::AutoSizeColumns( TStringGrid *pGrid )
{
TCanvas *pCanvas = StringGrid1->Canvas;
int *ColWidths = new int[pGrid->ColCount];
for( int Col = 0; Col < pGrid->ColCount; ++Col )
{
ColWidths[ Col ] = 0;
for( int Row = 0; Row < pGrid->RowCount; ++Row )
{
int Size = pCanvas->TextWidth( pGrid->Cells[Col][Row] );
if( Size>ColWidths[Col] ) ColWidths[ Col ] = Size;
}
}
::SendMessage( pGrid->Handle, WM_SETREDRAW, FALSE, 0 );
for( int Col = 0; Col < pGrid->ColCount; ++Col )
{
pGrid->ColWidths[ Col ] = ColWidths[ Col ];
}
// add code for setting Width and adjustments like ensuring it's all on screen ect..
delete [] ColWidths;
::SendMessage( pGrid->Handle, WM_SETREDRAW, TRUE, 0 );
pGrid->Invalidate();
}
//-------------------------------------------------------------
~ JD
 

Re:StringGrid - repainting problem

Quote
if( State.Contains(gdFixed) )
{
if( ACol == 0 && ARow == 0 ) pCanvas->Brush->Color = clSkyBlue;
else pCanvas->Brush->Color = pGrid->FixedColor;
}
Sorry, my mistake. I meant that selected cell and selected cell row and
column headers are clSkyBlue.
AutoSizeColumns function you wrote sizes column widths according to contents
length. In the procedure of article I was refering Stringgrid width is
devided between columns equally. Compination of these two would be the best.
First each column is sized according to its contents length then in the loop
X pixels is added to each column length so that the empty space on the rigth
is filled.
Pauli
 

Re:StringGrid - repainting problem

"Pauli" < XXXX@XXXXX.COM >wrote:
Quote
>if( State.Contains(gdFixed) )
>{
>if( ACol == 0 && ARow == 0 ) pCanvas->Brush->Color = clSkyBlue;
>else pCanvas->Brush->Color = pGrid->FixedColor;
>}

[...] I meant that selected cell and selected cell row and
column headers are clSkyBlue.
Just change the test:
if( State.Contains(gdFixed) )
{
if( ACol == pGrid->Col || ARow == pGrid->Row ) pCanvas->Brush->Color = clSkyBlue;
else pCanvas->Brush->Color = pGrid->FixedColor;
}
However, while the drawing will be correct, you'll find that
the timing of when it's drawn will not. So ... your choice of
Invalidate is the easiest to code to fix the timing but it's
also the least efficient. If you want to improve performance,
you could use the OnSelectCell again:
TRect R1 = StringGrid1->CellRect( 0, 0 ),
R2 = StringGrid1->CellRect( 0, pGrid->RowCount - 1 );
R1.bottom = R2.bottom;
// redraw all of the fixed columns
::InvalidateRect( StringGrid1->Handle, &R, FALSE );
R1.right = R2.right;
R1.bottom = R2.bottom;
// redraw all of the fixed rows
::InvalidateRect( StringGrid1->Handle, &R, FALSE );
Quote
AutoSizeColumns [...] Compination of these two would be the
best. [...]
Was there a question in there somewhere?
~ JD
 

Re:StringGrid - repainting problem

Quote
Was there a question in there somewhere?
No.
I put following test in the function you gave:
// start - test
int TotalColumnWidth = 0;
for (int Col = 0; Col < pGrid->ColCount; Col++)
TotalColumnWidth = TotalColumnWidth + pGrid->ColWidths[Col];
int GridClientWidth = pGrid->Width - GetSystemMetrics(SM_CXVSCROLL);
ShowMessage(IntToStr(TotalColumnWidth)); // in my case 576 pixels
ShowMessage(IntToStr(GridClientWidth)); // in my case 477 pixels
// end - test
The sum of column widths is 576 pxixels>GridWidth 477 pixels, but even
then there is empty space on the rigth (columns do not fill available grid
area). Why is that ?
Pauli
 

Re:StringGrid - repainting problem

"Pauli" < XXXX@XXXXX.COM >wrote:
Quote

[...] then there is empty space on the rigth
How stupid of me to not understand what you were saying:
//-------------------------------------------------------------
void __fastcall TForm1::AutoSizeColumns( TStringGrid *pGrid )
{
TCanvas *pCanvas = StringGrid1->Canvas;
int *ColWidths = new int[pGrid->ColCount];
for( int Col = 0; Col < pGrid->ColCount; ++Col )
{
ColWidths[ Col ] = 0;
for( int Row = 0; Row < pGrid->RowCount; ++Row )
{
int Size = pCanvas->TextWidth( pGrid->Cells[Col][Row] );
if( Size>ColWidths[Col] ) ColWidths[ Col ] = Size;
}
}
::SendMessage( pGrid->Handle, WM_SETREDRAW, FALSE, 0 );
int NewWidth = 1; // added
for( int Col = 0; Col < pGrid->ColCount; ++Col )
{
pGrid->ColWidths[ Col ] = ColWidths[ Col ];
NewWidth += ColWidths[ Col ]; // added
}
delete [] ColWidths;
// <Added>
if( pGrid->Options.Contains(goVertLine) ) NewWidth += pGrid->GridLineWidth * pGrid->ColCount;
if( pGrid->BorderStyle == bsSingle ) NewWidth += 2;
if( pGrid->ScrollBars == ssVertical || pGrid->ScrollBars == ssBoth )
{
int RowHeights = 1;
for( int Row = 0; Row < pGrid->RowCount; ++Row ) RowHeights += pGrid->RowHeights[ Row ];
if( pGrid->Options.Contains(goVertLine) ) RowHeights += pGrid->GridLineWidth * (pGrid->FixedRows + pGrid->VisibleRowCount);
if( pGrid->BorderStyle == bsSingle ) RowHeights += 2;
if( RowHeights>pGrid->Height ) NewWidth += ::GetSystemMetrics( SM_CXVSCROLL );
}
pGrid->Width = NewWidth;
// </Added>
::SendMessage( pGrid->Handle, WM_SETREDRAW, TRUE, 0 );
pGrid->Invalidate();
}
//-------------------------------------------------------------
~ JD
 

Re:StringGrid - repainting problem

Thanks JD, now it's perfect.
Pauli
 

Re:StringGrid - repainting problem

"Pauli" < XXXX@XXXXX.COM >wrote:
Quote

[...] now it's perfect.
Not quite. There's a cut-n-paste error in there:
Quote
// <Added>
[...]
if( pGrid->ScrollBars == ssVertical || pGrid->ScrollBars == ssBoth )
{
[...]
if( pGrid->Options.Contains(goVertLine) )
That should be goHorzLine instead.
~ JD