Textbox an die Länge der Eingabe anpassen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    Textbox an die Länge der Eingabe anpassen

    Hi,

    ich habe eine TextBox, nennen wir sie txtText ... die hat eine bestimmte Höhe und eine bestimmte Breite.

    In der TextChanged Prozedur möchte ich die Height und die Width der TextBox an die Eingabe anpassen.

    MIt der Höhe ist das einfach ... da gibt es txtText.Lines.Count ... und wenn ich die mit einem geeigneten Faktor multipliziere stimmt die Höhe.

    Aber die Breite der TextBox ist ein Problem ... ich kann zwar die Zeile mit der größten Anzahl von Zeichen ermitteln ... aber daraus kann man nicht die Länge der Zeile ermitteln, weil die Schrift proportional ist (und das soll auch so sein).

    Wie kann man denn die DISPLAYLÄNGE einer Zeichenkette ermitteln?

    LG
    Peter
    @Peter329 Du kannst Dir von der TextBox eine Graphics-Instanz holen.
    Mit dem Font der TextBox und dem Text kannst Du .MeasureString() machen und bekommst die Größe, die Du brauchst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Supi ... das geht ja einfacher als ich je gedacht hätte !

    Das klappt hervorragend ... es gibt nur ein kleines Problem: wenn man eine neue Zeile am Ende der Textbox beginnt (etwa durch Drücken von RETURN), dann wird dies nicht respektiert - die Höhe bleibt gleich und die erste Zeile verschwindet ! Deshalb nehme ich das Grafik Verfahren nur zum Berechnen der Breite ... zum Berechnen der Höhe verwende ich die Lines.Count Methode, weil die auch leere Zeilen am Ende der TextBox zählt.

    So funktioniert das jetzt wunderbar:

    VB.NET-Quellcode

    1. 'Adapt size of textbox to content
    2. Dim newSize As New Size
    3. Dim g As Graphics = txtText.CreateGraphics()
    4. Dim newSizeF As SizeF = g.MeasureString(txtText.Text, txtText.Font)
    5. If blnDebug Then Debug.Print("newSizeF heigth=" & newSizeF.Height.ToString & " width=" & newSizeF.Width)
    6. newSize.Width = CInt(Math.Ceiling(newSizeF.Width))
    7. If newSize.Width < MinimumWidth Then newSize.Width = MinimumWidth 'Dont let box disappear
    8. 'newSize.Height = CInt(Math.Ceiling(newSizeF.Height)) 'Dont use newSizeF.Height !
    9. 'If newSize.Height < 17 Then newSize.Height = 17
    10. Dim myCountLines As Integer = txtText.Lines.Count 'Use Lines.Count instead
    11. If blnDebug Then Debug.Print("myCountLines=" & myCountLines.ToString)
    12. If myCountLines < 1 Then myCountLines = 1 'Dont let box disappear
    13. newSize.Height = CInt(Math.Ceiling(CDbl(myCountLines) * CDbl(16.33886)))
    14. Return newSize


    Herzlichen Dank an die Ratgeber ... Daumen hoch ... und Problem gelöst ! :)

    LG
    Peter

    Peter329 schrieb:

    VB.NET-Quellcode

    1. CDbl(16.33886))
    • das ist bereits ein Double.
    • Wo kommt dieser numerische Wert her?
      Da solltest Du doch die Herkunft berücksichtigen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Peter329

    VB.NET-Quellcode

    1. Const MyPadding As Int32 = 6
    2. newSize.Height = CInt(myCountLines * Math.Ceiling(g.MeasureString("H", TextBox1.Font).Height) + MyPadding)
    Damit bist Du vom Font unabhängig. Der String "H" ist ein Dummy-Wert und wird genommen, damit LineBreaks nix verfälschen.
    Weiterhin: Wie verhält sich Dein Code bei Font-Änderungen? Ich denke, da gibt's Probleme mit dem Caret und der textausrichtung, wenn MinimumWidth überschritten wird.
    Also erst mal ganz herzlichen Dank dafür, dass ihr euch mit meinem Problem noch mal so eingehend befasst habt.

    Ich hab eure Vorschläge jetzt umgesetzt und die funktionieren auch super ! Selbst mit dem Ändern der Schriftgröße habe ich keine Probleme ...

    Aber was mir auffällt ... die Höhe der Textbox wächst schneller als ihr Inhalt ... je mehr Zeilen ich hinzufüge, desto deutlicher wird das ! Ich hab mal zwei Screenshots hinzugefügt, die zeigen, wie der freie Bereich am Ende der Box anwächst. Die Box ist zwar nur ein bissl zu groß ... aber es ist deutlich sichtbar!

    Die Anzahl der Zeilen wird richtig extrahiert ... irgendetwas scheint mit der Routine zum Berechnen der Höhe nicht zu stimmen.

    Vielleicht guckt ihr ja noch mal drüber ... Im Zweifel könnte ich damit leben !

    LG
    Peter
    Bilder
    • s 2017-04-01 13-00-443.jpg

      1,55 kB, 220×150, 1.286 mal angesehen
    • s 2017-04-01 13-01-140.jpg

      3,11 kB, 220×192, 1.342 mal angesehen

    Peter329 schrieb:

    die Höhe der Textbox wächst schneller als ihr Inhalt
    Es sieht so aus, als ob Du in Deiner Formel die Höhe einer Zeile multipliziert mit der Anzahl der Zeilen hast.
    Wenn die Zeile zu hoch ist, kommt genau das raus.
    Poste mal Deinen Code.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    na ja, ich habe den Vorschlag von @us4711 verwendet:

    VB.NET-Quellcode

    1. 'newSize.Height = CInt(Math.Ceiling(CDbl(myCountLines) * CDbl(16.33886)))
    2. Const MyPadding As Int32 = 6
    3. newSize.Height = CInt(myCountLines * Math.Ceiling(g.MeasureString("H", txtText.Font).Height) + MyPadding)


    Vermutlich ist die Multiplikation dafür verantwortlich ... die Rundung mit CInt und Math.Ceiling ist notwendig, weil newSize.Height ein Integer ist.

    Wenn ich die Sache debugge

    VB.NET-Quellcode

    1. Debug.Print((g.MeasureString("H", txtText.Font).Height).ToString)


    dann erhalte ich den Wert 16,33886 - das ist das was ich vorher als Zeilenhöhe per Hand ermittelt hatte ...


    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Peter329“ ()

    @Peter329 Sag ich doch.
    Poste mal so viel Code, dass ich ihn testen kann.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Peter329 Probier mal, alle Zeilen einzeln auszumessen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim g As Graphics = txtText.CreateGraphics()
    2. Dim hh As Single = 0
    3. For Each line In txtText.Lines
    4. Dim line2 = line
    5. If String.IsNullOrWhiteSpace(line) Then
    6. line2 = "a"
    7. End If
    8. Dim sz = g.MeasureString(line2, txtText.Font)
    9. hh += sz.Height
    10. Next
    11. Dim newSize = txtText.Size
    12. newSize.Height = CInt(hh)
    13. txtText.Size = newSize
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich denke, ich habe herausgefunden woran es liegt. Es muss heißen:

    VB.NET-Quellcode

    1. newSize.Height = CInt(Math.Ceiling(myCountLines * g.MeasureString("H", txtText.Font).Height + MyPadding))


    MyPadding kann man übrigens nicht weglassen, sonst erhält man recht merkwürdige Effekte beim Eingeben der ersten Zeile, wenn man den Font vergrößert.

    Jetzt klappt die Routine endlich !

    Herzlichen Dank nochmal an alle Ratgeber!

    LG
    Peter
    @Peter329
    Irgendwie hat's mir nicht gefallen, mit festen Werten beim Padding zu arbeiten. Hab' daher nochmals ein bischen gebastelt, und bin auf nachstehende Lösung gekommen, die auch vereinfacht ist:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private MinimumWidth As Int32 = Nothing
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. AddHandler Me.txtText.TextChanged, AddressOf textText_TextChanged
    5. AddHandler Me.txtText.FontChanged, AddressOf textText_TextChanged
    6. Me.MinimumWidth = Me.txtText.Width
    7. Me.txtText.Multiline = True
    8. Me.txtText.AcceptsReturn = True
    9. End Sub
    10. Private Sub textText_TextChanged(sender As Object, e As EventArgs)
    11. Dim size As Size = TextRenderer.MeasureText(txtText.Text, txtText.Font)
    12. If size.Width <= MinimumWidth Then
    13. txtText.Width = MinimumWidth
    14. Else
    15. txtText.Width = size.Width + 3
    16. End If
    17. If size.Height < txtText.PreferredHeight Then
    18. txtText.Height = txtText.PreferredHeight
    19. Else
    20. txtText.Height = size.Height + txtText.PreferredHeight - TextRenderer.MeasureText("H", txtText.Font).Height
    21. End If
    22. End Sub
    23. End Class

    //Edit: Anpassung an leeren Text ergänzt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „us4711“ ()

    Ich bin erst jetzt dazu gekommen, die Lösung auszuprobieren. Und sie funktioniert HERVORRAGEND !

    Was mir besonders gefällt: die Sache ist jetzt klar strukturiert und verwendet keinen "Mischmasch" aus "Measure" und "Lines". Leerzeilen am Ende werden richtig erkannt !

    So sollte man diese Aufgaben lösen !

    Herzlichen Dank nochmal !

    LG
    Peter