Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Tmemo height dynamisch an text anpassen (https://www.delphipraxis.net/153452-tmemo-height-dynamisch-text-anpassen.html)

H3llsing 3. Aug 2010 16:04

Delphi-Version: 2010

Tmemo height dynamisch an text anpassen
 
Tag zusammen,

ich erzeuge dynamisch ein Panel, das Top Align auf einer scrollbox erzeugt wird, darauf erzeuge ich ein Tmemo mit text, der auch Zeilenumbrüche -> #$D#$A oder anders -> #10#13, #10, #13 enthält.

Dieses Memofeld soll Alclient mit Margin 10 auf dem Panel ausgerichtet werden und die HÖHE des Panels soll sich dem Text entsprechend anpassen, so dass keine Scrollbar innerhalb des Memos nötig ist.

WordWrap soll ebenfalls eingeschaltet werden, was die höhenberechnung meiner meinung nach schwierig bis unmöglich ?, macht.


Hier meine erzeugung der Panels und Memos
Delphi-Quellcode:

//ar_content = array der beiträge
//ar_content[i].Content = Text mit umbrüchen;

  for i := 0 to length(ar_content)-1  do
  begin

    //neues Panel erzeugen
    newPanel := Tpanel.Create(frmAnfrage);
    newPanel.ParentColor := False;
    newPanel.ParentBackground := false;

    newPanel.Parent := frmAnfrage.ScrollBox1;
    newPanel.ParentDoubleBuffered := true;
    newPanel.Align := alTop;

    //hier müsste die Höhe angepasst werden
    NewPanel.Height := AdjustMemoHeight(ar_content[i].Content);


    //neues MEmo auf dem Panel erzeugen
    newMemo := Tmemo.Create(frmAnfrage);
    newMemo.Font.Name := 'Courier New';
    newMemo.parent := newPanel;
    newMemo.WordWrap := true;
    newMemo.text := ar_content[i].Content;
    newMemo.ReadOnly := true;

    newMemo.Margins.SetBounds(10,10,10,10);
    NewMemo.AlignWithMargins:= true;
    newmemo.Align := alClient;
    newMemo.ParentDoubleBuffered := true;
Dann hab ich mich schon mit etlichen versuchen in die Depression gestürzt :?
hier einer der vielen ansätze die höhe herauszubekommen,jedoch erfolglos.
Entweder ist das Panel zu Groß oder zu klein ;/


Delphi-Quellcode:
//################################################
//   AdjustMemoHeight
//   passt die größe des memos an den text an
//################################################
function TfrmSalesboard.AdjustMemoHeight(S: String):integer;
var
  höhe     : integer;
  i        : Integer;
  txtLaenge : integer;
  teiler   : integer;
  posi     : integer;
  anzahl   : integer;
  S2        : string;
  lastPosi : integer;
Begin
   
   
   s := StringReplace(S,#10#13,'|_|',[rfReplaceAll,rfIgnoreCase]);
   s := StringReplace(S,#10,'|_|',[rfReplaceAll,rfIgnoreCase]);
   s := StringReplace(S,#13,'|_|',[rfReplaceAll,rfIgnoreCase]);
 

   Anzahl := 1; //anzahl der Lines


   //3 mal = Absatz mit Leerzeile ?
   //2 mal = einfacher Umbruch ?
   while pos('|_||_||_|',s) <> 0 do
   begin
     s := StringReplace(S,'|_||_||_|','|$|',[rfIgnoreCase]);
     anzahl := anzahl+2;
   end;

   while pos('|_||_|',s) <> 0 do
   begin
     s := StringReplace(S,'|_||_|','|$|',[rfIgnoreCase]);
     anzahl := anzahl+1;
   end;

   lastposi := 0;
   while pos('|$|',s) <> 0 do
   begin
     posi := pos('|$|',s);
     s2 := copy(s,lastposi,posi-1);
     lastposi := posi+3;
     s := StringReplace(S,'|$|',' ',[rfIgnoreCase]);
     if length(s2) > 65 then
       anzahl := anzahl+1;
   end;

   s2 := copy(s,0,posi-1);


       txtLaenge := length(s);
       //teiler   := round(txtLaenge / 70);
       höhe     := ((anzahl)*25)+20;  // 25 = linehöhe ????   20 = für die 10 Margin Top und bottom des Memos
    showmessage(intToStr(höhe));

  result := höhe;
end;


ich hoffe jemand von euch hat einen guten Tipp. Oder sauberen Code und nich wie den oben ;)

blackfin 3. Aug 2010 16:15

AW: Tmemo height dynamisch an text anpassen
 
Erstelle eine unsichtbares Label mit fester Breite und wordwrap, gib dem Label die gleichen Schrift-Einstellungen wie das Memo, dann kannst du
den Canvas vom Label verwenden, um die Texthöhe herauszufinden:

Delphi-Quellcode:
H := Label1.Canvas.TextHeight(Meintext);
Memo1.Height := H ;

H3llsing 3. Aug 2010 16:25

AW: Tmemo height dynamisch an text anpassen
 
hab ich auch schon versucht, allerdings war dann das memo trotzdem zu hoch.
liegt es eventuell daran dass ich das Label auch als AlTop in die scrollbox gesetzt habe, danach autosize,wordwrap an
und dann den Text zugewiesen habe.


hab den Code leider schon verworfen, probiers aber gern nochmal aus ;)


btw: es geht darum die größe des Panels dynamisch zu erzeugen.

blackfin 3. Aug 2010 16:29

AW: Tmemo height dynamisch an text anpassen
 
Alternativ probier noch folgendes, auch indem du den Canvas des Labels benutzt.
Das Label muss aber die gleiche Breite (oder etwas kleiner) wie das Memo dafür haben, Höhe ist egal.

Delphi-Quellcode:
..
.
var
  h: Integer ;
  FRect : TRect ;
begin
  FRect.Left := 0;
  FRect.Top := 0 ;
  FRect.Right := Label1.Width ;
  FRect.Bottom := 0 ;

  DrawText(Label1.Canvas.Handle, PChar(MeinText),Length(MeinText),FRect,DT_CALCRECT or DT_NOPREFIX or DT_WORDBREAK);
  Memo1.Height := FRect.Bottom;

  Memo1.Text := MeinText ;
end;

H3llsing 3. Aug 2010 16:34

AW: Tmemo height dynamisch an text anpassen
 
vielen dank erstmal, werde es morgen früh ausprobieren ;)

himitsu 3. Aug 2010 16:49

AW: Tmemo height dynamisch an text anpassen
 
Zitat:

Zitat von blackfin (Beitrag 1039283)
Das Label muss aber die gleiche Breite (oder etwas kleiner)

Eher kleiner, da das Memo bestimmt einen Rand/Border hat.

DeddyH 3. Aug 2010 17:05

AW: Tmemo height dynamisch an text anpassen
 
Jetzt hab ich mich auch mal daran versucht (allerdings ohne Hilfslabel):
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var aDC: hDC;
    R: TRect;
    aCanvas: TCanvas;
begin
  R := Memo1.BoundsRect;
  Memo1.Lines.BeginUpdate;
  try
    Memo1.Lines.Add('Zeile');
    Memo1.Text := trim(Memo1.Text);
    aDC := GetWindowDC(Memo1.Handle);
    if aDC <> 0 then
      try
        aCanvas := TCanvas.Create;
        try
          aCanvas.Handle := aDC;
          aCanvas.Font.Assign(Memo1.Font);
          DrawText(aCanvas.Handle,PChar(Memo1.Text),-1,R,DT_NOPREFIX or DT_CALCRECT or DT_WORDBREAK);
        finally
          aCanvas.Free;
        end;
      finally
        ReleaseDC(Memo1.Handle,aDC);
      end;
  finally
    Memo1.Lines.EndUpdate;
  end;
  Memo1.Height := R.Bottom - R.Top + 6;
end;
Ob das jetzt 100%-ig so stimmt, weiß ich nicht (vor allem die 6 in der letzten Zeile), aber das sah bei ersten Tests ganz gut aus.

wicht 3. Aug 2010 19:49

AW: Tmemo height dynamisch an text anpassen
 
Wegen der 6 in der letzten Zeile eventuell nicht Height, sondern ClientHeight zuweisen? Hab's aber nicht getestet...

H3llsing 4. Aug 2010 09:09

AW: Tmemo height dynamisch an text anpassen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Vielen Dank schomal für euer bemühen, aber es will einfach nicht so funktionieren wie es soll.

Die MEthode von DaddyH kommt mit nem MEmo alleine ganz gut ran, aber ich möchte ja, dass das Memo AlClient bleibt, das heißt die größe des memos passt sich dem des PARENT Panels an. Nun möchte ich dem Panel die größe geben, die das memo eigentlich benötigt + 20 px margin, damit das auch passt.
Zusätzlich ist mir aufgefallen, wenn ich die Größe des PArentPanels nach dem MEmo.Create verändere, dass dann die Erstellungsreihenfolge komplett verschoben wird :(

ich hänge mal screenshots zur verdeutlichung an.



Delphi-Quellcode:
    //neues Panel erzeugen
    newPanel := Tpanel.Create(frmAnfrage);
    newPanel.ParentColor := False;
    newPanel.ParentBackground := false;

    newPanel.Parent := frmAnfrage.ScrollBox1;
    newPanel.ParentDoubleBuffered := true;
    newPanel.Align := alTop;
  //  newHeight := AdjustMemoHeight(ar_content[i].Content);
    NewPanel.Height := 1000;

    newLab := Tlabel.Create(newPanel);
    newLab.Parent := newPanel;
    NewLab.Visible := false;
    newlab.Margins.SetBounds(10,10,10,10);
    newLab.Align := alClient;
    NewLab.Font.Name := 'Courier New';
    newLab.Caption := ar_content[i].Content;

    LH := newLab.Canvas.TextHeight(ar_content[i].Content);
    NewPanel.Height := LH ;
Das hat leider auch nicht funktioniert, irgendwie ist das Panel dann nur ein paar pixel groß und wird in der Erstellungsreihenfolge nach oben gesetzt. Ich erzeuge vor dem Panel auf dem das Memo liegt noch ein blaues Panel mit der überschrift,dass auch auf der scrollbox liegt.
Bei dieser MEthode ist nun das graue Panel mit Memo über das blaue gerutscht.

HIER die Falsche Reihenfolge, wenn ich die größe des Panels später ändere
Anhang 31786


Hier wie es fast aussehen sollte, hier wurde ne feste PanelBReite vergeben.
Es Sollte aber auf die Texthöhe eingestellt werden
Anhang 31787

DeddyH 4. Aug 2010 09:22

AW: Tmemo height dynamisch an text anpassen
 
Du kannst doch statt des Memos das umgebende Panel in der Höhe ändern, das Memo sollte sich doch dann daran anpassen. Und was meinst Du mit "Erstellungsreihenfolge verschoben"?

himitsu 4. Aug 2010 09:31

AW: Tmemo height dynamisch an text anpassen
 
Ich hätt wohl einfach einen TWebBrowser verwendet und dieses über eine HTML-Tabelle gelöst. :angle2:

H3llsing 4. Aug 2010 09:37

AW: Tmemo height dynamisch an text anpassen
 
Erstellungsreihenfolge siehe Anhang, die beiden Bilder.
Die panels schieben sich dann nach oben komischer weise.

Zu der höhe des Panels, ich habe versucht mit der Funktion die Höhe des Panels entsprechend der länge des memotextes zu berechnen, aber irgendwo hakt es.
Ich möchte ja keien Scrollbars verwenden ;)

@himitsu
joar das wäre natürlich auch ne Lösung, und ich glaube ich werds auch direkt so ausprobieren :)

himitsu 4. Aug 2010 09:55

AW: Tmemo height dynamisch an text anpassen
 
Delphi-Quellcode:
//Memo1.ScrollBars := ssNone; // im OI
//Memo1.WordWrap := True;

procedure TForm5.Memo1Change(Sender: TObject);
var
  S: String;
  i: Integer;
begin
  S := Memo1.Text;
  if (S = '') or (Copy(S, Length(S))[1] in [#13, #10]) then i := 1 else i := 0;
  Memo1.ClientHeight := Canvas.TextHeight('X') * (Memo1.Lines.Count + i) + 3;
end;
oder
Delphi-Quellcode:
procedure TForm5.Memo1Change(Sender: TObject);
var
  S: String;
  i: Integer;
begin
  S := Memo1.Text;
  if (S <> '') and (Copy(S, Length(S))[1] in [#13, #10]) then i := 1 else i := 0;
  Memo1.ClientHeight := Canvas.TextHeight('X') * Max(Memo1.Lines.Count + i, 1) + 3;
end;
+3 = irgendein krummer und nicht näher beschreibbarer Erfahrungswert :roll:
i = da in .Lines eine leere Zeile am Ende nicht mitgezählt wird
.Lines enthält auch die virtuellen Zeilenumbrüche (blöderweise, aber hier isses mal praktisch)

das Form.Canvas hatte ich jetzt nur genommen, da ich ja irgendein Canvas brauchte und meine Testform zufällig die selbe Schriftart hatte, wie das Memo (das kann auch irgendein anderes Canvas sein und notfalls auch ein selbsterstelltes, welchem der Font des Memos zugewiesen wurde)

bei so vielen Memos würde ich statt Memo1 eher TMemo(Sender) verwenden und allen Memos die selbe Ereignisprozedur spendieren.

Die Höhe ( Canvas.TextHeight('X') ) könnte man auch nur einmal am Anfang berechnen und in einer Variable speichern.

H3llsing 4. Aug 2010 10:38

AW: Tmemo height dynamisch an text anpassen
 
Ok Vielen dank an alle.
Ich hab mich nun für die TWebbrowser komponente entschieden, dann werd ich gleich noch jquery mit einbaun und dann sieht das ganze auch nett aus =)
nur leider nicht schön da ich für jeden Benutzer ein eigenes Temp.html anlegen muss, wo der Browser hinnavigiert.
aber bei 14 usern geht das gaze schon.

Ich werd später nochmal mein Code posten für die, die es interessiert ;)

himitsu 4. Aug 2010 11:06

AW: Tmemo height dynamisch an text anpassen
 
Im Forum finden sich etliche Beispiele, wie man den HTML-Code direkt an den TWebBrowser übergeben kann (anstatt zu eine HTML-Datei zu navigieren)

CSS, Images und Co. kann man inline in den HTML-Code einbetten, so benötigt man auch dafür keine externe Datei.
Irgendwo hatte ich auch mal die Link-Definition, um externe Dateien (CSS, Bilder usw.) auch vom System aus den Resourcen einer EXE/DLL laden zu lassen.

H3llsing 4. Aug 2010 14:38

AW: Tmemo height dynamisch an text anpassen
 
Liste der Anhänge anzeigen (Anzahl: 1)
vielen dank, werd ich mir später nochmal anschauen mit dem direkten senden.

hier ist vorerst meine Lösung, die ich nochmal überarbeiten werde sobald ich zeit habe =)
Delphi-Quellcode:
 try
    Mylist := Tstringlist.Create;
    Mylist.LoadFromFile(ExtractFilePath(ParamStr(0))+'html/anfang.html');
    for i := 0 to length(ar_content)-1  do
    begin

      try
        MYBeitrag := Tstringlist.Create;
        MYBeitrag.LoadFromFile(ExtractFilePath(ParamStr(0))+'html/Beitrag.html');
        if FormatDateTime('dd.mm.yyyy',now) = FormatDateTime('dd.mm.yyyy',ar_content[i].datum) then
           header := '#'+intToStr(i+1)+' von '+ar_content[i].ersteller+' um <b>'+FormatDateTime('HH:MM:ss',ar_content[i].datum)+'</b> geschrieben'
        else
           header := '#'+intToStr(i+1)+' von '+ar_content[i].ersteller+' um <b>'+FormatDateTime('dd.mm.yyyy HH:MM:ss',ar_content[i].datum)+'</b> geschrieben';

        ar_content[i].content := StringReplace(ar_content[i].content,#10#13,'<br>',[rfReplaceAll,rfIgnoreCase]);
        ar_content[i].content := StringReplace(ar_content[i].content,#10,'<br>',[rfReplaceAll,rfIgnoreCase]);

        MYBeitrag.Text := StringReplace(MYBeitrag.Text,'[header]',header,[rfReplaceAll,rfIgnoreCase]);
        MYBeitrag.Text := StringReplace(MYBeitrag.Text,'[nr]',intToStr(i+1),[rfReplaceAll,rfIgnoreCase]);
        MYBeitrag.Text := StringReplace(MYBeitrag.Text,'[content]',trim(ar_content[i].content),[rfReplaceAll,rfIgnoreCase]);


        MYlist.Add(trim(MYBeitrag.Text));
      finally
        MYBeitrag.Free;
      end;


    end;
    Mylist.Add('</table></body></html>');
    mylist.SaveToFile(ExtractFilePath(ParamStr(0))+'html/'+Benutzer+'temp.html');
    frmAnfrage.WebBrowser1.Navigate(ExtractFilePath(ParamStr(0))+'html/'+Benutzer+'temp.html');
  finally
    Mylist.Free;
  end;
und hier noch ein kleiner screen
Anhang 31791


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:17 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz