Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - TRichEdit / TRichMemo: Text unsichtbar und wieder sichtbar


galagher - Fr 29.05.20 18:55
Titel: TRichEdit / TRichMemo: Text unsichtbar und wieder sichtbar
Hallo!

Wie ich in einem TRichEdit (oder bei Lazarus in einem TRichMemo) Text "unsichtbar" mache, weiss ich:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure HideText;
var
  Format: TCharFormat2;
begin
  FillChar(Format, SizeOf(Format), 0);

  with Format do
  begin
    cbSize := SizeOf(Format);
    dwMask := CFM_HIDDEN;
    dwEffects := CFM_HIDDEN;
  end;

  Perform(EM_SETCHARFORMAT, SCF_SELECTION, LPARAM(@Format));
end;

Aber wie mache ich den Text wieder sichtbar?


Moderiert von user profile iconTh69: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Sa 30.05.2020 um 08:39


mandras - Fr 29.05.20 19:18

ich rate einfach mal:

...
dwMask := CFM_HIDDEN;
dwEffects := 0;
...


galagher - Fr 29.05.20 19:53

user profile iconmandras hat folgendes geschrieben Zum zitierten Posting springen:
ich rate einfach mal:

...
dwMask := CFM_HIDDEN;
dwEffects := 0;
...
Leider klappt das nicht. Denn: Wie "bekomme" ich denn den unsichtbaren Text? SelLength und SelText funktionieren da ja nicht. Ich kann den unsichtbaren Text zwar wieder anzeigen, indem ich Lines[CaretPos.Y] ändere (es genügt, Lines[CaretPos.Y := Lines[CaretPos.Y] anzugeben), dabei gehen aber die RTF-Textattribute verloren.

Aber ich werde es nochmal testen...

Edit:
Klappt! Danke! Hatte wohl vorhin irgendeinen Fehler im Code!


galagher - Do 04.06.20 20:21

user profile icongalagher hat folgendes geschrieben Zum zitierten Posting springen:
Leider klappt das nicht. Denn: Wie "bekomme" ich denn den unsichtbaren Text? SelLength und SelText funktionieren da ja nicht.
Das gilt bei Delphi's TRichEdit, da muss SelLength mindestens die Länge des unsichtbaren Textes haben, aber per Code lässt sich das nicht ermitteln.

Ich könnte natürlich einfach die Textlänge als Parameter an meine Prozedur übergeben, aber bei jeder Änderung am Text muss ich dann zuerst die neue Textlänge ermittlen und dann diesen Wert korrigieren. Das ist fehleranfällig.

Wie also bekomme ich die Länge eines nicht sichtbaren Textes, wenn ich weiss, an welcher Stelle er beginnt und wenn ich auch weiss, dass nach seinem Ende immer eine Leerzeile folgt? Ich komm' nicht drauf...


galagher - Fr 05.06.20 19:54

Ich habe es jetzt hinbekommen!

Hier eine immerhin funktionierende Prozedur, kann aber sicher noch optimiert werden! Probleme gibt's nur mit WordWrap, wenn die Zeilenlängen zu eng zusammengestaucht sind. Aber vielleicht schaffe ich auch das noch. Es klappt auch mit TJvRichEdit von den Jedis, nicht aber mit TCustomRichEdit.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
procedure TForm1.Textfolding(const aRichEdit: TRichEdit;
  const cCollapse, cExpand: Char; const aFontName: String);
var
  i, n, iSelStart, y: Integer;

  procedure HideText(const aRichEdit: TRichEdit);
  var
    Format: TCharFormat2;
  begin
    FillChar(Format, SizeOf(Format), 0);

    with Format do
    begin
      cbSize := SizeOf(Format);
      dwMask := CFM_HIDDEN;
      dwEffects := CFM_HIDDEN;
    end;

    SendMessage(aRichEdit.Handle, EM_SETCHARFORMAT, SCF_SELECTION, LPARAM(@Format));
  end;

  procedure ShowText(const aRichEdit: TRichEdit);
  var
    Format: TCharFormat2;
  begin
    FillChar(Format, SizeOf(Format), 0);

    with Format do
    begin
      cbSize := SizeOf(Format);
      dwMask := CFM_HIDDEN;
      dwEffects := 0;
    end;

    SendMessage(aRichEdit.Handle, EM_SETCHARFORMAT, SCF_SELECTION, LPARAM(@Format));
  end;

begin  {Textfolding}
  with aRichEdit do
  begin
    if CaretPos.X = 0 then
    begin
      if (Length(Lines[CaretPos.Y]) = 0then
        exit;

      if (Lines[CaretPos.Y][1] = cCollapse) or (Lines[CaretPos.Y][1] = cExpand) then
      begin
        n := 0;
        iSelStart := SelStart;
        y := CaretPos.Y;

        Lines.BeginUpdate;

        try
          {Wenn kein Fontname angegeben, gilt jeder Font}
          if not (aFontName = ''then
          begin
            SelLength := 1;
            if not (AnsiLowerCase(SelAttributes.Name) = AnsiLowerCase(aFontName)) then
              exit;
          end;
          SelLength := 0;

          {Anzahl der Zeilen bis zur nächsten Leerzeile ermitteln und SelLength setzen}
          if (Lines[CaretPos.Y][1] = cCollapse) then
            for i := CaretPos.Y+1 to Lines.Count-1 do
            begin
              if Lines[i] = '' then
                break;

              Inc(n, Length(Lines[i])+1);  {Mit +1 Zeilenumbruch einbeziehen}
            end;

          if (Lines[CaretPos.Y][1] = cCollapse) then
          begin
            SelLength := 1;
            SelText := cExpand;

            SelStart := SelStart+Length(Lines[CaretPos.Y]);
            SelLength := n;

            if n > 0 then
              while SelText[Length(SelText)] <> #13 do
                SelLength := SelLength-1;

            HideText(aRichEdit);
          end
          else
          begin
            SelLength := 1;
            SelText := cCollapse;

            SelStart := SelStart+Length(Lines[y]);

            n := SelStart;
            for i := y-1 to Lines.Count-1 do
            begin
              if Lines[i] = '' then
                break;

              Inc(n);
            end;

            SelLength := Length(Lines[y+1]);
            if SelLength = 0 then
              exit;

            for i := n downto iSelStart do
              if SelText[Length(SelText)] <> #13 then
                SelLength := SelLength-1;

            ShowText(aRichEdit);
          end;

        finally
          SelStart := iSelStart;
          SelLength := 0;
          Lines.EndUpdate;
        end;
      end;
    end;  {if CaretPos.X = 0}
  end;
end;


Edit:
Die Prozedur war immer noch fehlerhaft (SelLength war beim wieder sichtbarmachen des Textes zu lang oder, je nach SelStart, zu kurz), ich habe sie daher aktualisiert.

Edit:
Nach Zuweisung von SelLength := n: und SelLength := Length(Lines[y+1]); noch jeweils SelText prüfen:

Delphi-Quelltext
1:
2:
 if (SelText = ''or not (SelText[Length(SelText)] = #13then
   exit;