开发者

How do I determine the height of a line of text in a TMemo programmatically?

开发者 https://www.devze.com 2023-03-22 23:56 出处:网络
I\'ve got a TMemo, and I want to always make it exactly high enough to display the number of lines it contains.Unfortunately, I don\'t quite know how to calculate that.I can\'t base it off the 开发者_

I've got a TMemo, and I want to always make it exactly high enough to display the number of lines it contains. Unfortunately, I don't quite know how to calculate that. I can't base it off the 开发者_如何学C.Font.Size property, because that will vary based on DPI. And I can't use TCanvas.TextHeight because TMemo doesn't seem to have a canvas.

Anyone know how to do this right?


I see a problem, i think all lines on a TMemo are equal on height, but some can be empty...

So getting Height of empty ones will give zero while they are not zero height on the TMemo.

So the solution maybe doing something like Memo.Lines.Count*LineHeight

Beware that the Lineheight may not be getted by Canvas.TextHeight since Canvas.TextHeight will give more or less exact height of minimal height for a text... i mean it will not give same height for text 'ABC' than for 'ABCp', etc...

I would recomend (if not want to call Windows API) to use Font.Height, but if it is negative the internal leading of each line is not measured...

So i would recomend to do the next steps (tested):

  • Asign a positive value for Memo.Font.Height on the OnCreate event or anywhere you want, with this the lineheight ot the TMemo will be such value you asigned
  • Total height now can be obtained directly by Memo.Lines.Count*LineHeight, since you have asigned a positive value to Memo.Font.Height (remember that would make Memo.Font.Size to be negative)

Personally i do this on the TForm OnCreate event (to ensure it is done only once), just to ensure visual font size is not changed and MyMemo.Font.Height includes internal leading of each line:

MyMemo.Font.Height:=Abs(MyMemo.Font.Size*MyMemo.Font.PixelsPerInch div Screen.PixelsPerInch);

Ensure the previous to be done only once, otherwise the text size will be visaully bigger and bigger, as much as times you run it... it is caused because not allways MyMemo.Font.PixelsPerInch is equal to Screen.PixelsPerInch... in my case they are 80 and 96 respectively.

Then, when i need to know line height i just use:

Abs(MyMemo.Font.Height*Screen.PixelsPerInch div 72)

That gives exact height of one TMemo line, since all lines have the same height, the total height would be:

MyMemo.Lines.Count*Abs(MyMemo.Font.Height*Screen.PixelsPerInch div 72)

So, to make TMemo height as big as its contained text i do this (read the comment of each line, they are very important):

MyMemo.Font.Height:=Abs(MyMemo.Font.Size*MyMemo.Font.PixelsPerInch div Screen.PixelsPerInch); // I do this on the Tform OnCreate event, to ensure only done once
MyMemo.Height:=1+MyMemo.Lines.Count*Abs(MyMemo.Font.Height*Screen.PixelsPerInch div 72); // I do this anywhere after adding the text and/or after editing it

I you do not want to play with Screen.PixelsPerInch you can just do this (read the comment of each line, they are very important):

MyMemo.Font.Height:=Abs(MyMemo.Font.Height); // This may make text size to visually change, that was why i use the corrector by using MyMemo.Font.PixelsPerInch and Screen.PixelsPerInch
MyMemo.Height:=1+MyMemo.Lines.Count*Abs(MyMemo.Font.Height*MyMemo.Font.PixelsPerInch div 72);

Hope this can help anyone.


You can write your own implementation of TCanvas.TextHeight for TMemo:

function CountMemoLineHeights(Memo: TMemo): Integer;
var
  DC: HDC;
  SaveFont: HFont;
  Size: TSize;
  I: Integer;

begin
  DC:= GetDC(Memo.Handle);
  SaveFont:= SelectObject(DC, Memo.Font.Handle);
  Size.cX := 0;
  Size.cY := 0;
//  I have not noticed difference in actual line heights for TMemo,
//    so the next line should work OK
  Windows.GetTextExtentPoint32(DC, 'W', 1, Size);
//  BTW next (commented) line returns Size.cY = 0 for empty line (Memo.Lines[I] = '') 
//    Windows.GetTextExtentPoint32(DC, Memo.Lines[I], Length(Memo.Lines[I]), Size);
  Result:= Memo.Lines.Count * Size.cY;
  SelectObject(DC, SaveFont);
  ReleaseDC(Memo.Handle, DC);
end;


You need to use a TCanvas for this. You can either create a TBitMap in the background and use its TCanvas (after assigning the Memo's Font property to the Bitmap.Canvas' one), or use a TCanvas from another component. Somthing like this:

BMP:=TBitMap.Create;
TRY
  BMP.Canvas.Font.Assign(Memo.Font);
  TotalHeight:=0;
  FOR LineNo:=1 TO Memo.Lines.Count DO INC(TotalHeight,BMP.Canvas.TextHeight(Memo.Lines[PRED(I)]))
FINALLY
  FreeAndNIL(BMP)
END;

Edit:

Or perhaps this one:

BMP:=TBitMap.Create;
TRY
  BMP.Canvas.Font.Assign(Memo.Font);
  LineHeight:=BMP.Canvas.TextHeight('Wq');
  TotalHeight:=Memo.Lines.Count*LineHeight
FINALLY
  FreeAndNIL(BMP)
END;


I originally suggested looing at the "Lines" TStrings list member in TMemo.

Instead, please look at the "Font" member in the parent class, TCustomEdit.

'Hope that helps .. PS

0

精彩评论

暂无评论...
验证码 换一张
取 消