How to put CR/LF into a TStringgrid cell? - delphi

I want to have one fixed row as a header, but the texts are rather long, so I'd like to increase the row height and insert CR/LF into the cell text.
Googling shows this as a solution (and it's the first thing I thought of before googling), but it doesn't seem to work. Any ideas?
Grid.Cells[2,3] := 'This is a sample test' + #13#10 + 'This is the second line';
What happens is that the cell contains This is a sample testThis is the second line
I am using Delphi 7 if it makes any difference.
[Bounty] "My bad. I actually awarded this an answer two years ago without checking and now find that the answer did not work. Apologies to anyone who was misled. This is a frequently asked, often wrongly answered question."
I presume that we are looking to use OnDrawCell, but imagine that we would also have to increase the height of the string grid row which contains the cell.
I will award the answer for either code or a FOSS VCL component.
[Update] must work with Delphi XE2 Starter edition

TStringGrid uses Canvas.TextRect, which uses ExtTextOut, which in turn does not support drawing of multiline text.
You have to draw this yourself in an OnDrawCell event handler with WinAPI's DrawText routine. See for example this answer on how to use DrawText for multiline text, and this recent answer on how to implement custom drawing in OnDrawCell:
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
procedure FormCreate(Sender: TObject);
procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
private
procedure FillWithRandomText(AGrid: TStringGrid);
procedure UpdateRowHeights(AGrid: TStringGrid);
end;
procedure TForm1.FillWithRandomText(AGrid: TStringGrid);
const
S = 'This is a sample'#13#10'text that contains'#13#10'multiple lines.';
var
X: Integer;
Y: Integer;
begin
for X := AGrid.FixedCols to AGrid.ColCount - 1 do
for Y := AGrid.FixedRows to AGrid.RowCount - 1 do
AGrid.Cells[X, Y] := Copy(S, 1, 8 + Random(Length(S) - 8));
UpdateRowHeights(AGrid);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FillWithRandomText(StringGrid1);
end;
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
with TStringGrid(Sender) do
if Pos(#13#10, Cells[ACol, ARow]) > 0 then
begin
Canvas.FillRect(Rect);
Inc(Rect.Left, 2);
Inc(Rect.Top, 2);
DrawText(Canvas.Handle, PChar(Cells[ACol, ARow]), -1, Rect,
DT_NOPREFIX or DT_WORDBREAK);
end;
end;
procedure TForm1.UpdateRowHeights(AGrid: TStringGrid);
var
Y: Integer;
MaxHeight: Integer;
X: Integer;
R: TRect;
TxtHeight: Integer;
begin
for Y := AGrid.FixedRows to AGrid.RowCount - 1 do
begin
MaxHeight := AGrid.DefaultRowHeight - 4;
for X := AGrid.FixedCols to AGrid.ColCount - 1 do
begin
R := Rect(0, 0, AGrid.ColWidths[X] - 4, 0);
TxtHeight := DrawText(AGrid.Canvas.Handle, PChar(AGrid.Cells[X, Y]), -1,
R, DT_WORDBREAK or DT_CALCRECT);
if TxtHeight > MaxHeight then
MaxHeight := TxtHeight;
end;
AGrid.RowHeights[Y] := MaxHeight + 4;
end;
end;
There are also other StringGrid components able of drawing multiline text. For instance, this one which I wrote myself (download source: NLDStringGrid) with possibly this result:
var
R: TRect;
begin
NLDStringGrid1.Columns.Add;
NLDStringGrid1.Columns.Add;
NLDStringGrid1.Cells[1, 1] := 'Sample test'#13#10'Second line';
NLDStringGrid1.Columns[1].MultiLine := True;
NLDStringGrid1.AutoRowHeights := True;
SetRect(R, 2, 2, 3, 3);
NLDStringGrid1.MergeCells(TGridRect(R), True, True);
NLDStringGrid1.ColWidths[2] := 40;
NLDStringGrid1.Cells[2, 2] := 'Sample test'#13#10'Second line';
end;

The TStringGrid's default renderer don't support multiple lines. By setting the TStringGrid in OwnerDraw mode (by invoking the OnDrawCell event) you can render each cell by your own liking.
Have a look at this for an example that helped a previous user.
Linked reference code inserted:
procedure DrawSGCell(Sender : TObject; C, R : integer; Rect : TRect;
Style : TFontStyles; Wrap : boolean; Just : TAlignment;
CanEdit : boolean);
{ draws formatted contents in string grid cell at col C, row R;
Style is a set of fsBold, fsItalic, fsUnderline and fsStrikeOut;
Wrap invokes word wrap for the cell's text; Just is taLeftJustify,
taRightJustify or taCenter; if CanEdit false, cell will be given
the background color of fixed cells; call this routine from
grid's DrawCell event }
var
S : string;
DrawRect : TRect;
begin
with (Sender as tStringGrid), Canvas do begin
{ erase earlier contents from default drawing }
if (R >= FixedRows) and (C >= FixedCols) and CanEdit then
Brush.Color:= Color
else
Brush.Color:= FixedColor;
FillRect(Rect);
{ get cell contents }
S:= Cells[C, R];
if length(S) > 0 then begin
case Just of
taLeftJustify : S:= ' ' + S;
taRightJustify : S:= S + ' ';
end;
{ set font style }
Font.Style:= Style;
{ copy of cell rectangle for text sizing }
DrawRect:= Rect;
if Wrap then begin
{ get size of text rectangle in DrawRect, with word wrap }
DrawText(Handle, PChar(S), length(S), DrawRect,
dt_calcrect or dt_wordbreak or dt_center);
if (DrawRect.Bottom - DrawRect.Top) > RowHeights[R] then begin
{ cell word-wraps; increase row height }
RowHeights[R]:= DrawRect.Bottom - DrawRect.Top;
SetGridHeight(Sender as tStringGrid);
end
else begin
{ cell doesn't word-wrap }
DrawRect.Right:= Rect.Right;
FillRect(DrawRect);
case Just of
taLeftJustify : DrawText(Handle, PChar(S), length(S), DrawRect,
dt_wordbreak or dt_left);
taCenter : DrawText(Handle, PChar(S), length(S), DrawRect,
dt_wordbreak or dt_center);
taRightJustify : DrawText(Handle, PChar(S), length(S), DrawRect,
dt_wordbreak or dt_right);
end;
end
end
else
{ no word wrap }
case Just of
taLeftJustify : DrawText(Handle, PChar(S), length(S), DrawRect,
dt_singleline or dt_vcenter or dt_left);
taCenter : DrawText(Handle, PChar(S), length(S), DrawRect,
dt_singleline or dt_vcenter or dt_center);
taRightJustify : DrawText(Handle, PChar(S), length(S), DrawRect,
dt_singleline or dt_vcenter or dt_right);
end;
{ restore no font styles }
Font.Style:= [];
end;
end;
end;
I think this will work fine for you...

Related

Stringgrid wordwrap displays text twice

I want to display a long string in a string grid cell in a wordwrap format and found the following code to do so:
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
//enable wordwrap in cells
var
S: String;
drawrect :trect;
begin
stringgrid1.Canvas.FillRect (Rect);
S:= (Sender As TStringgrid).Cells [ACol, ARow ];
If Length(S) > 0 Then Begin
drawrect := rect;
DrawText((Sender As TStringgrid).canvas.handle,
Pchar(S), Length(S), drawrect,
dt_calcrect or dt_wordbreak or dt_left );
If (drawrect.bottom - drawrect.top) >
(Sender As TStringgrid).RowHeights[Arow]
Then
(Sender As TStringgrid).RowHeights[Arow] :=
(drawrect.bottom - drawrect.top)
// changing the row height fires the event again!
Else Begin
drawrect.Right := rect.right;
(Sender As TStringgrid).canvas.fillrect( drawrect );
DrawText((Sender As TStringgrid).canvas.handle,
Pchar(S), Length(S), drawrect,
dt_wordbreak or dt_left);
End;
End;
end;
The word wrap works but the cell displays both the original text and the wrapped text. I assume that that since this is an onDrawCell event that the original text is already drawn and I would erase it with the first line of code (stringgrid1.canvas.fillrect(rect), but this has no effect on the display. What am I missing?
...would erase it with the first line of code
(stringgrid1.canvas.fillrect(rect), but this has no effect on the
display
That is because you did not select the Brush.Style (and possibly also Brush.Color) just before calling FillRect() f.ex.
stringgrid1.canvas.Brush.Style := bsSolid; // add this line
stringgrid1.canvas.Brush.Color := clWhite; // add this line
stringgrid1.Canvas.FillRect (Rect);
You will notice that there are some remnants from the default drawing at the left edge of the cells (not visible in the image below, I already fixed them). That is because the TStringGrid internally offsets the cell drawing with 4 pixels. To change that you need to counter offset the Rect parameter with -4 and grow the width with +4, also before calling FillRect().
After above changes the grid looks very flat and dull (in the image I already added colors to the second grid). To reinstate some color differances for the header column and header row, you need to treat cells with gdFixed in State with a different Brush.Color. The same for cells with gdSelected in State.
The above is what you also need to do if you untick DefaultDrawing. The second grid and accompanying code demonstrates this. Note that the code also includes a call to DrawFocusRect when gdFocused in State just before the end.
Note that I replaced all those Sender as TStringGrid with a local grid variable.
procedure TForm4.StringGrid2DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
var
grid: TStringGrid;
S: String;
drawrect: TRect;
bgFill: TColor;
begin
grid := Sender as TStringGrid;
if gdFixed in State then
bgFill := $FFF8F8
else
if gdSelected in State then
bgFill := $FFF0D0
else
bgFill := clWhite;
grid.Canvas.Brush.Color := bgFill;
grid.canvas.Brush.Style := bsSolid;
grid.canvas.fillrect(Rect);
S := grid.Cells[ACol, ARow];
if Length(S) > 0 then
begin
drawrect := Rect;
drawrect.Inflate(-4 , 0);
DrawText(grid.canvas.handle, Pchar(S), Length(S), drawrect,
dt_calcrect or dt_wordbreak or dt_left);
If (drawrect.bottom - drawrect.top) > grid.RowHeights[ARow] then
grid.RowHeights[ARow] := (drawrect.bottom - drawrect.top+2)
// changing the row height fires the event again!
else
begin
drawrect.Right := Rect.Right;
// grid.canvas.fillrect(drawrect);
DrawText(grid.canvas.handle, Pchar(S), Length(S), drawrect,
dt_wordbreak or dt_left);
end;
end;
if gdFocused in State then
grid.Canvas.DrawFocusRect(Rect);
end;
The third grid demonstrates the easiest and IMO the best solution, wich is to skip the TStringGrid altogether and use TDrawGrid instead. You must keep the data you want to show in the grid, separately somewhere. I defined an array: s_arr: array of array of string;.
In this case you can leave DefaultDrawing on because the TDrawGrid doesn't draw any text during the default drawing, the content drawing takes place only in the OnDrawCell event.
procedure TForm4.DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
var
S: string;
grid: TDrawGrid;
drawrect: TRect;
begin
grid := Sender as TDrawGrid;
S := s_arr[ACol, ARow];
If Length(S) > 0 Then
Begin
drawrect := Rect;
drawrect.Inflate(-4 , 0);
DrawText(grid.canvas.handle, Pchar(S), Length(S), drawrect,
dt_calcrect or dt_wordbreak or dt_left);
If (drawrect.bottom - drawrect.top) > grid.RowHeights[ARow] Then
grid.RowHeights[ARow] := (drawrect.bottom - drawrect.top + 2)
// changing the row height fires the event again!
Else
Begin
drawrect.Right := Rect.Right;
grid.canvas.fillrect(drawrect);
DrawText(grid.canvas.handle, Pchar(S), Length(S), drawrect, dt_wordbreak or dt_left);
End;
End;
end;
Leaving DefaultDrawing on, all those theme related features are drawn by the grid itself and we only draw the text on top.

Using Windows API DrawText with DT_PATH_ELLIPSIS

I'm trying to use the Windows API function DrawText but don't get the results I expect. Maybe I'm doing something wrong here, but as I read the documentation I really don't see the problem. I'm using the following code.
function GetEllipsisString(Font: TFont; const Text: string;
Width: integer): String;
var
DC: HDC;
SaveFont: HFont;
R: TRect;
begin
DC := GetDC(0);
try
SaveFont := SelectObject(DC, Font.Handle);
R := Rect (0, 0, Width-1, 0);
Result := Text+' ';
Winapi.Windows.DrawtextW (DC, PChar(Result), Length(Result), R,
DT_CALCRECT+DT_LEFT+DT_PATH_ELLIPSIS+DT_MODIFYSTRING);
SelectObject(DC, SaveFont);
finally
ReleaseDC(0, DC);
end;
end;
DT_PATH_ELLIPSIS just doesn't seem to do anything. I tried it with DT_END_ELLIPSIS and that gave me some result (see example). When I give the paramater "Text" a string with a backslash (\) in it, it does seem to set the ellipsis but the function ignores the rect measurements.
Example
Text = 'This text has to many characters to fit.'
DT_END_ELLIPSIS returns 'This text has to m...'#0'characters to fit. '
DT_PATH_ELLIPSIS returns 'This text has to many characters to fit. '
Here's a procedure that draws a string with an ellipsis in the middle if the string is too wide for the given rect:
procedure DrawTextWithMiddleEllipsis(Canvas: TCanvas; Text: string; DrawRect:
TRect; Flags: Integer);
var
S, LastS: string;
R: TRect;
Sz: TSize;
RectWidth, I: Integer;
begin
S := Text;
R := DrawRect;
GetTextExtentPoint32(Canvas.Handle, S, Length(S), Sz);
RectWidth := DrawRect.Right - DrawRect.Left;
if Sz.cx > RectWidth then
begin
//The string is too wide. Need to cut it down with ellipsis
//Start with the smallest possible truncated-and-ellipsis-modified string,
//and expand until we have the biggest one that can fit
S := '...';
for I := 1 to Length(Text) div 2 do
begin
LastS := S;
//Get the first I chars, then the ellipsis, then the last I chars
S := Copy(Text, 1, I) + '...' + Copy(Text, Length(Text) - I + 1, I);
GetTextExtentPoint32(Canvas.Handle, S, Length(S), Sz);
if Sz.cx > RectWidth then
begin
DrawText(Canvas.Handle, LastS, Length(LastS), DrawRect, Flags);
Break;
end;
end;
end else
//The string will fit in the width of the given rect, don't mess with it
DrawText(Canvas.Handle, S, Length(S), DrawRect, Flags);
end;
Here's an example of how its called (PaintBox1 is a TPaintBox):
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
S: string;
R: TRect;
begin
S := 'This is extra long text that should overflow';
R := PaintBox1.ClientRect;
DrawTextWithMiddleEllipsis(PaintBox1.Canvas, S, R, DT_LEFT or DT_TOP);
end;
Based on #jthurman code
Advantages:
*simplified,
*more universal
*fixes a bug
Enjoy.
{ Takes a long string and truncates it in the middle. Example: '123...789' }
function GetEllipsisText(CONST s: string; Canvas: TCanvas; MaxWidth: Integer; Flags: Integer= DT_LEFT or DT_TOP): string;
var
NewStr, LastStr: string;
TextSize: TSize;
EllipsisSize: Integer;
begin
NewStr := '...';
EllipsisSize:= Canvas.TextWidth(NewStr);
GetTextExtentPoint32(Canvas.Handle, s, Length(s), TextSize);
if TextSize.cX > MaxWidth
then
//Start with the smallest possible truncated-and-ellipsis-modified string, and expand until we have the biggest one that can fit
for VAR i:= 1 to Length(s) div 2 do
begin
LastStr := NewStr;
NewStr := Copy(s, 1, I) + '...' + Copy(s, Length(s) - I + 1, I); // Get the first I chars, then the ellipsis, then the last I chars
GetTextExtentPoint32(Canvas.Handle, NewStr, Length(NewStr), TextSize);
if TextSize.cx > (MaxWidth - EllipsisSize)
then Exit(LastStr);
end
else
Result:= s; //The string will fit in the width of the given rect, don't mess with it
end;

Shadows under StringGrid

I have a form. On formshow, I initialize values of a field into stringgrid cells, but it shows a shadow under cell's texts.
I've used Persian charaters for field's values.
I did the same with english values, but it works fine.
I appreciate any suggestions.
example of the output:
With enaabled DefaultDrawing the text will be already rendered if you enter OnDrawCell.
Since you are calculating the needed rowheight in painting using DT_CALCRECT of DrawText you will have to calculated the Rect wich shall be filled/cleared with FillRect.
You can use UnionRect to get the final Rect which has to be filled (FillRect in the example).
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1.Cells[1,1] := 'Hallo'#13'World';
StringGrid1.Cells[2,2] := 'اهای' +13# + 'جهان';
end;
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
S:String;
drawrect,Fillrect : TRect;
begin
s := (Sender as TStringGrid).Cells[ACol, ARow];
drawrect := Rect;
DrawText((Sender as TStringGrid).Canvas.handle, Pchar(s), Length(s),
drawrect, DT_CALCRECT or DT_WORDBREAK or DT_LEFT);
if (drawrect.bottom - drawrect.Top) > (Sender as TStringGrid)
.RowHeights[ARow] then (Sender as TStringGrid)
.RowHeights[ARow] := (drawrect.bottom - drawrect.Top);
UnionRect(FillRect,Rect,DrawRect);
(Sender as TStringGrid).Canvas.FillRect(FillRect);
DrawText((Sender as TStringGrid).Canvas.handle, Pchar(s), Length(s),
drawrect, DT_WORDBREAK or DT_LEFT);
end;

Add graphical bar to a StringGrid col

Using Delphi 2010 and a TStringGrid component, I currently display five filds from a database query.
Here is a simplied example of what i am doing
//set up the grid
procedure TGriddata.FormCreate(Sender: TObject);
begin
grdMain.Rows[0].commatext:='"One","Two","Three","Four","Five"';
grdMain.ColWidths[0]:= 50;
grdMain.ColWidths[1]:= 175;
grdMain.ColWidths[2]:= 175;
grdMain.ColWidths[3]:= 100;
grdMain.ColWidths[4]:= 300;
end;
//display the data in the grid
//note, I am not showing my creation, execution, or destroy of the query
procedure TGriddata.load;
begin
...
grdMain.Cells[0,row]:= FieldByName('one').AsString;
grdMain.Cells[1,row]:= FieldByName('two').AsString;
grdMain.Cells[2,row]:= FieldByName('three').AsString;
grdMain.Cells[3,row]:= FieldByName('four').AsString;
//draw progress bar here
...
end;
One of the columns ("Five") needs to display a navy blue horizontal bar in the col. It should also diplay some text centered in the bar. I have no expereince using the custom drawing. What properties do i set to only custom draw the one column and use the default drawing for the other columns?
Add the text to the cells like you normally would. But you have to draw those bars in the OnDrawCell event. Leave DefaultDrawing as is (True by default), and erase the already drawn cell text in those columns by filling it in advance:
procedure TForm1.grdMainDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
Progress: Single;
R: TRect;
Txt: String;
begin
with TStringGrid(Sender) do
if (ACol = 4) and (ARow >= FixedRows) then
begin
Progress := StrToFloatDef(Cells[ACol, ARow], 0) / 100;
Canvas.FillRect(Rect);
R := Rect;
R.Right := R.Left + Trunc((R.Right - R.Left) * Progress);
Canvas.Brush.Color := clNavy;
Canvas.Rectangle(R);
Txt := Cells[ACol, ARow] + '%';
Canvas.Brush.Style := bsClear;
IntersectClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
Canvas.Font.Color := clHighlightText;
DrawText(Canvas.Handle, PChar(Txt), -1, Rect, DT_SINGLELINE or
DT_CENTER or DT_VCENTER or DT_END_ELLIPSIS or DT_NOPREFIX);
SelectClipRgn(Canvas.Handle, 0);
ExcludeClipRect(Canvas.Handle, R.Left, R.Top, R.Right, R.Bottom);
Canvas.Font.Color := clWindowText;
DrawText(Canvas.Handle, PChar(Txt), -1, Rect, DT_SINGLELINE or
DT_CENTER or DT_VCENTER or DT_END_ELLIPSIS or DT_NOPREFIX);
SelectClipRgn(Canvas.Handle, 0);
end;
end;
For more options, you might consider this DrawStatus routine.
Here you can view a sample (Draw percentage in a cell in a Grid), to draw a bar in a cell of a TStringGrid.
The explanation is in spanish, but you can download the code, that is very simple.
Also you can use authomatic translation on right of page.
procedure TFormDrawCell.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
const
STR_EMPTY = '';
CHAR_PERCENT = '%';
SPACE_TO_CENTER_CELLTEXT = 0;
var
fValue: Integer;
ActualPenColor, ActualBrushColor: TColor;
EmptyDS: Boolean;
DrawRect: TRect;
fWidth1, fLeft2: Integer;
StrValue: string;
begin
if not (Column.FieldName = 'Precent') then
Exit;
if not (cbdraw.Checked) then
Exit;
EmptyDS := ((TDBGrid(Sender).DataSource.DataSet.EoF) and
(TDBGrid(Sender).DataSource.DataSet.Bof));
if (Column.Field.IsNull) then begin
fValue := -1;
StrValue := STR_EMPTY;
end
else begin
fValue := Column.Field.AsInteger;
StrValue := IntToStr(fValue) + CHAR_PERCENT;
end;
DrawRect := Rect;
InflateRect(DrawRect, -1, -1);
fWidth1 := (((DrawRect.Right - DrawRect.Left) * fValue) DIV 100);
ActualPenColor := TDBGrid(Sender).Canvas.Pen.Color;
ActualBrushColor := TDBGrid(Sender).Canvas.Brush.Color;
TDBGrid(Sender).Canvas.Pen.Color := clHighlight;
TDBGrid(Sender).Canvas.Brush.Color := clWhite;
TDBGrid(Sender).Canvas.Rectangle(DrawRect);
if (fValue > 0) then begin
TDBGrid(Sender).Canvas.Pen.Color := clSkyBlue;
TDBGrid(Sender).Canvas.Brush.Color := clSkyBlue;
DrawRect.Right := DrawRect.Left + fWidth1;
InflateRect(DrawRect, -1, -1);
TDBGrid(Sender).Canvas.Rectangle(DrawRect);
end;
if not (EmptyDS) then begin
DrawRect := Rect;
InflateRect(DrawRect, -2, -2);
TDBGrid(Sender).Canvas.Brush.Style := bsClear;
fLeft2 := DrawRect.Left + (DrawRect.Right - DrawRect.Left) shr 1 -
(TDBGrid(Sender).Canvas.TextWidth(StrValue) shr 1);
TDBGrid(Sender).Canvas.TextRect(DrawRect, fLeft2,
DrawRect.Top + SPACE_TO_CENTER_CELLTEXT, StrValue);
end;
TDBGrid(Sender).Canvas.Pen.Color := ActualPenColor;
TDBGrid(Sender).Canvas.Brush.Color := ActualBrushColor;
end;
Regards.

How can I get the new height of a wrapedtext of DrawText();?

code sample
procedure TForm1.Button1Click(Sender: TObject);
var
r: Trect;
s: String;
begin
R := Rect(0,0, 300, 100);
s := 'WordWrapTextOut(TargetCanvas: TCanvas; var x, y: integer; S: string; maxwidth, lineheight: integer);';
DrawText(Canvas.Handle, PChar(s), length(s), R, DT_WORDBREAK or DT_LEFT);
end;
I want to wrap the text in 300px width but how can I get the new Height? Is there a way or any solution?
The height of the drawn text is the returned value of DrawText.
HeightOfText := DrawText(...
If you want to update your rectangle before drawing the text you could use DT_CALCRECT. DrawText will then modify your rectangle to the new height (and width if necessary). If you only need the height though use the return value as Andreas Rejbrand showed.
Here's a sample of this:
procedure TForm1.Button1Click(Sender: TObject);
var
r: Trect;
s: String;
begin
R := Rect(0,0, 300, 100);
s := 'WordWrapTextOut(TargetCanvas: TCanvas; var x, y: integer; S: string; maxwidth, lineheight: integer);';
if DrawText(Canvas.Handle, PChar(s), length(s), R, DT_CALCRECT or DT_WORDBREAK or DT_LEFT) <> 0 then
begin
DrawText(Canvas.Handle, PChar(s), length(s), R, DT_WORDBREAK or DT_LEFT);
r.Top := r.Bottom;
r.Bottom := r.Bottom * 2;
DrawText(Canvas.Handle, PChar(s), length(s), R, DT_WORDBREAK or DT_LEFT);
end;
end;
I would recommend reading the docs for more details:
http://msdn.microsoft.com/en-us/library/dd162498(v=vs.85).aspx
As was mentioned here you can get it by calling DrawText function with DT_CALCRECT flag specified what actually won't paint anything; it just calculates appropriate rectangle and returns it to variable R.
procedure TForm1.Button1Click(Sender: TObject);
var
R: TRect;
S: String;
begin
R := Rect(0, 0, 20, 20);
S := 'What might be the new high of this text ?';
DrawText(Canvas.Handle, PChar(S), Length(S), R, DT_WORDBREAK or DT_LEFT or DT_CALCRECT);
ShowMessage('New height might be '+IntToStr(R.Bottom - R.Top)+' px');
end;
What means if you call it twice using the following example, you'll get drawn the wrapped text. It's because the first call with DT_CALCRECT calculates the rectangle (and modify R variable by doing it) and the second call draws the text in that modified rectangle area.
procedure TForm1.Button1Click(Sender: TObject);
var
R: TRect;
S: String;
begin
R := Rect(0, 0, 20, 20);
S := 'Some text which will be stoutly wrapped and painted :)';
DrawText(Canvas.Handle, PChar(S), Length(S), R, DT_WORDBREAK or DT_LEFT or DT_CALCRECT);
DrawText(Canvas.Handle, PChar(S), Length(S), R, DT_WORDBREAK or DT_LEFT);
end;

Resources