4

I found that this question has been asked before for VCL, but I haven't had any luck getting the answers for that question to work on a Firemonkey TMemo.

I've noticed that memo.Lines.Count always seems to match the line count based off how many I add, not as they're formatted (the memo does have wordwrap turned on). Without knowing that number I'm not sure how to start figuring this out.

Any ideas?

Edit: The width of the memo will depend on the orientation of the device, obviously if the width changes the number of lines showing could change. Also, I'd like to not alter the font of the memo.

Community
  • 1
  • 1
Sentient
  • 1,102
  • 12
  • 29

4 Answers4

6
Procedure ResizeMemo(AMemo: TMemo);
const
  Offset = 4; //The diference between ContentBounds and ContentLayout
begin
  AMemo.Height := AMemo.ContentBounds.Height + Offset;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ResizeMemo(Memo1);
end;
Agustin Seifert
  • 1,938
  • 1
  • 16
  • 29
  • 1
    That worked great. I'm glad there was a simple solution. – Sentient Feb 24 '14 at 17:07
  • 2
    Just a FYI: I found when trying to resize my memo in a `FormResize` event that the `ContentBounds` had been invalidated. Adding a `Memo.RealignContent` before the resize fixed that. – Sentient Feb 24 '14 at 18:52
1

The following function should give you what you want. It does not change anything in the memo and takes into account the font and any line breaks and wraps. If setting the memo height to the calculated value, you will need to add a few pixels for borders to eliminate scrollbars.

(add fmx.text to uses statement for XE3, might be different for other versions as they keep changing things with each release)

function get_memo_height(amemo:tmemo):single;

var i:integer;
    astring:string;
    layout:ttextlayout;

begin
  Layout := TTextLayoutManager.DefaultTextLayout.Create;
  astring:='';
  for i:=0 to amemo.lines.count-1 do astring:=astring+amemo.lines[i]+chr(10);
  Layout.BeginUpdate;
  Layout.Text :=astring;
  Layout.WordWrap := amemo.wordwrap;
  Layout.HorizontalAlign := amemo.TextAlign;
  Layout.MaxSize := PointF(amemo.width-amemo.VScrollBar.width,maxint);
  Layout.VerticalAlign := tTextAlign.taLeading;
  Layout.Font := amemo.Font;
  Layout.TopLeft := pointf(0,0);
  Layout.EndUpdate;
  result:=layout.textrect.bottom;
  Layout.free;
end;
David Peters
  • 220
  • 1
  • 10
  • I'm using XE5 and there doesn't seem to be a memo.VScrollBar property. – Sentient Feb 24 '14 at 17:09
  • use the contentbounds solution for XE4,XE5 but that won't work for XE3. You practically have to rewrite anything in Firemonkey at every release hence I still use XE3 even though I have maintenance. – David Peters Feb 24 '14 at 17:30
1

Here's my new attempt:

FMX:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 305
  ClientWidth = 333
  FormFactor.Width = 1920
  FormFactor.Height = 1080
  FormFactor.Devices = [dkDesktop]
  DesignerMobile = False
  DesignerWidth = 0
  DesignerHeight = 0
  DesignerDeviceName = ''
  DesignerOrientation = 0
  DesignerOSVersion = ''
  object Memo1: TMemo
    Touch.InteractiveGestures = [igPan, igLongTap, igDoubleTap]
    Anchors = [akLeft, akTop, akRight]
    Height = 257.000000000000000000
    Position.X = 8.000000000000000000
    Position.Y = 8.000000000000000000
    TabOrder = 0
    Width = 312.000000000000000000
    Lines.Strings = (

        'Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 L' +
        'ine 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 '
      'Line 2 Line 2 Line 2 Line 2 Line 2 '
      ''
      'Line 4'

        'Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 L' +
        'ine 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Li' +
        'ne 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Lin' +
        'e 5 Line 5 Line 5 Line 5 Line 5 ')
    Font.Family = 'Arial'
    WordWrap = True
  end
  object Button1: TButton
    Anchors = [akLeft, akTop, akRight]
    Height = 22.000000000000000000
    Position.X = 8.000000000000000000
    Position.Y = 272.000000000000000000
    TabOrder = 1
    Text = 'Show Line Count'
    Width = 312.000000000000000000
    OnClick = Button1Click
  end
  object Memo3: TMemo
    Touch.InteractiveGestures = [igPan, igLongTap, igDoubleTap]
    Height = 50.000000000000000000
    Position.X = 176.000000000000000000
    Position.Y = 184.000000000000000000
    TabOrder = 2
    Visible = False
    Width = 100.000000000000000000
    Lines.Strings = (
      '1')
  end
end

PAS:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Rtti, System.Classes,
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Layouts,
  FMX.Memo, FMX.Text, FMX.StdCtrls, FMX.TextLayout;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Memo3: TMemo;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
    layout: TTextLayout;
    cont, LineHeight : real;
begin
  cont := 0;
  layout:= TTextLayout(memo3.Lines.Objects[0]);
  LineHeight := layout.TextHeight;
  for i:= 0 to memo1.Lines.Count-1 do
  begin
    layout:= TTextLayout(memo1.Lines.Objects[i]);
    if Assigned(layout) then
    begin
      cont := cont + (layout.TextHeight / LineHeight);
    end;
  end;
  showmessage('Line count according to firemonkey: ' + inttostr(memo1.Lines.Count));
  showmessage('Real line count: ' + VarToStr(cont));
end;

end.

Hope it Helps.

Filipe.Fonseca
  • 321
  • 2
  • 14
0

This is, of course, not elegant, but you can move the caret to the end of your TMemo, and then ask for the CaretPosition:

function getLastMemoLineNumber(const memo: TMemo): Integer;
var
    oldCaretPosition: TCaretPosition;
begin
    Assert( Assigned(memo) );
    oldCaretPosition := memo.CaretPosition;

    try
        memo.GoToTextEnd();
        Result := memo.CaretPosition.Line;
    finally
        memo.CaretPosition := oldCaretPosition;
    end;
end;
  • 1
    Unfortunately this has the same problem. The result I get is the number of lines I have added, not the number of lines being shown in the memo. Any lines that are a result of wordwrap aren't counted. – Sentient Feb 17 '14 at 19:27
  • @Sentient: It works for me on XE5. What Delphi/FireMonkey version is yours? How are you inserting your text? I entered it by typing on the keyboard and and a long line that caused one or several linewraps only counted as one. – Günther the Beautiful Feb 18 '14 at 07:23
  • I think you are describing what I see as the problem. If I add one line that gets wrapped around twice so it actually shows as 3, I have no way of knowing how big the memo actually should be. What I need to know to resize the memo is how many lines are being displayed. – Sentient Feb 18 '14 at 17:44
  • To answer your questions fully: XE5 Update 2. I set `Memo.Text := mystring;` (my string is several strings put together with sLineBreak) – Sentient Feb 18 '14 at 17:46