9

In Delphi i wish to draw text inside a TRect. I am hoping for the following functionality:

  1. Draw the text centred vertically within the TRect
  2. Draw the text centred horizontally within the TRect
  3. If there is space for more than 1 line of text (using TRect's height), draw the text multiline
  4. If the text does not fit in the TRect (either on a single or mult line) then append ellipsis to the text.

I can see the Windows.DrawText() function almost covers this functionality, however when writing text, multiline and vertically centred are mutually exclusive.

I was wondering if this functionality is built into windows (2000+)? If not is there a way to do this without writing my own function?

Simon
  • 9,197
  • 13
  • 72
  • 115

2 Answers2

23

Sorry, this is a combination of all previous answers and comments. But it seems OP needs more assistance.

function DrawTextCentered(Canvas: TCanvas; const R: TRect; S: String): Integer;
var
  DrawRect: TRect;
  DrawFlags: Cardinal;
  DrawParams: TDrawTextParams;
begin
  DrawRect := R;
  DrawFlags := DT_END_ELLIPSIS or DT_NOPREFIX or DT_WORDBREAK or
    DT_EDITCONTROL or DT_CENTER;
  DrawText(Canvas.Handle, PChar(S), -1, DrawRect, DrawFlags or DT_CALCRECT);
  DrawRect.Right := R.Right;
  if DrawRect.Bottom < R.Bottom then
    OffsetRect(DrawRect, 0, (R.Bottom - DrawRect.Bottom) div 2)
  else
    DrawRect.Bottom := R.Bottom;
  ZeroMemory(@DrawParams, SizeOf(DrawParams));
  DrawParams.cbSize := SizeOf(DrawParams);
  DrawTextEx(Canvas.Handle, PChar(S), -1, DrawRect, DrawFlags, @DrawParams);
  Result := DrawParams.uiLengthDrawn;
end;

procedure TForm1.FormPaint(Sender: TObject);
const
  S = 'This is a very long text as test case for my paint routine.';
var
  R: TRect;
begin
  SetRect(R, 100, 100, 200, 140);
  Canvas.Rectangle(R);
  InflateRect(R, -1, -1);
  Caption := Format('%d characters drawn', [DrawTextCentered(Canvas, R, S)]);
end;
NGLN
  • 43,011
  • 8
  • 105
  • 200
  • I cant see the difference between deamon_x's and this version but this works!!!! Thanks :) – Simon Aug 10 '11 at 11:49
  • This line is the key: DT_END_ELLIPSIS or DT_NOPREFIX or DT_WORDBREAK or DT_EDITCONTROL or DT_CENTER; – Simon Aug 10 '11 at 11:57
  • 2
    i know turned out to be something simple, but any other combination of the flags didnt work! Woul split the points if i could :) – Simon Aug 10 '11 at 12:00
  • @NGLN I hoped your example would solve my issue, but it doesn't, I try to v-center the text within a specified rectangle but if the text doesn't fit and the ELLIPSIS is drawn, the DrawRect.Bottom reports a value representing 3 lines even though only 2 lines are clearly drawn. Here is a sample image showing the issue http://zoomplayer.com/pix/font_vcenter.jpg (The darker area behind the text is the specified rectangle). – bLight Jun 05 '17 at 18:59
  • @bLight I think it is best to ask a new question for your specific problem. – NGLN Jun 07 '17 at 06:15
5

Measure the text first using DT_CALCRECT. Pass DT_WORDBREAK to specify that word wrapping is enabled. This will allow you to find the required height for your text. Then you can, in your code, calculate the vertical offset that gives you vertically centred text, and draw to that offset.

Andriy M
  • 76,112
  • 17
  • 94
  • 154
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I tried this method, but it appears for some reason the DT_WORD_ELLIPSIS is ignored when DT_WORDBREAK is specified. This routine currently does not paint text outside of the rect (and doesnt show the '...') – Simon Aug 10 '11 at 09:57
  • Is there a way i can get exactly which text (or length) was drawn? – Simon Aug 10 '11 at 09:59
  • @Simon With `DT_WORDBREAK` all the text is drawn. You aren't seeing it presumably due to clipping. – David Heffernan Aug 10 '11 at 10:13
  • @David, Simon - you need to specify also the `DT_MODIFYSTRING` flag –  Aug 10 '11 at 10:27
  • Its very close to what i want, however if i shrink the TRect, the text therefore cannot fit in the TRect and i would expect an ellipsis to be shown. However this is not the case, the last word(s) are simply not drawn. I dont think DT_NOCLIP is what i am after – Simon Aug 10 '11 at 10:28
  • I have also looked at the DrawTextParams data structure of DrawTextEx, however the uiLengthDrawn which i beleive should return the number of chars drawn always returns the length of the string? – Simon Aug 10 '11 at 10:31
  • @Simon As I said I think all the text is drawn and what you can't see is due to clipping. – David Heffernan Aug 10 '11 at 10:35
  • 1
    @Simon DrawTextParams.uiLengthDrawn returns the number of characters drawn, including the 3 points due to DT_WORD_ELLIPSIS. – NGLN Aug 10 '11 at 10:48
  • 2
    @Simon - I think you're looking for `DT_CENTER or DT_WORDBREAK or DT_END_ELLIPSIS or DT_MODIFYSTRING` this will wrap your text, center it horizontally and display ellipsis at the end of the text in case the last line cannot be displayed entirely. –  Aug 10 '11 at 10:49
  • 1
    You do not need DT_MODIFYSTRING, that's only needed for text handling after painting. Use DT_EDITCONTROL to display only full visible lines. Without, there are partial lines drawn which may be the reason for not seeing the ellipsis. – NGLN Aug 10 '11 at 10:53
  • @NGLN - feel free to copy the code I've posted. Better will be to show the example than discuss here. –  Aug 10 '11 at 10:56