Wie kann ich zeigen, Bildlaufleisten auf einem System.Windows.Forms.TextBox nur, wenn der Text nicht paßt?

StackOverflow https://stackoverflow.com/questions/73110

  •  09-06-2019
  •  | 
  •  

Frage

Für eine System.Windows.Forms.TextBox mit Multiline = True, ich würde nur gerne die Bildlaufleisten zeigen, wenn der Text nicht paßt.

Dies ist eine Nur-Lese-Textbox nur für die Anzeige verwendet. Es ist ein TextBox, so dass Benutzer den Text heraus kopieren. Gibt es etwas, integrierte Auto Show von Bildlaufleisten zu unterstützen? Wenn nicht, sollte ich sein, eine andere Steuerung verwenden? Oder muss ich Textchanged einzuhaken und manuell für Überlauf überprüfen (wenn ja, wie zu sagen, ob der Text passt?)


hat kein Glück mit verschiedenen Kombinationen von WordWrap und Scroll-Leisten-Einstellungen. Ich mag keine Scrollbalken am Anfang haben, und jeder dynamisch erscheint nur, wenn der Text nicht in der vorgegebenen Richtung paßt.


@nobugz, danke, das funktioniert, wenn WordWrap deaktiviert ist. Ich würde nicht lieber wordwrap deaktivieren, aber es ist das kleinere Übel.


@ André Neves, guter Punkt, und ich würde diesen Weg gehen, wenn es vom Benutzer editierbar ist. Ich bin damit einverstanden, dass die Konsistenz der Grundregel für UI Anschaulichkeit ist.

War es hilfreich?

Lösung

eine neue Klasse zu einem Projekt hinzufügen und den Code unten einfügen. Kompilieren. Lassen Sie die neue Steuerung aus der oben in der Toolbox auf das Formular. Es ist nicht ganz perfekt, aber sollte für Sie arbeiten.

using System;
using System.Drawing;
using System.Windows.Forms;

public class MyTextBox : TextBox {
  private bool mScrollbars;
  public MyTextBox() {
    this.Multiline = true;
    this.ReadOnly = true;
  }
  private void checkForScrollbars() {
    bool scroll = false;
    int cnt = this.Lines.Length;
    if (cnt > 1) {
      int pos0 = this.GetPositionFromCharIndex(this.GetFirstCharIndexFromLine(0)).Y;
      if (pos0 >= 32768) pos0 -= 65536;
      int pos1 = this.GetPositionFromCharIndex(this.GetFirstCharIndexFromLine(1)).Y;
      if (pos1 >= 32768) pos1 -= 65536;
      int h = pos1 - pos0;
      scroll = cnt * h > (this.ClientSize.Height - 6);  // 6 = padding
    }
    if (scroll != mScrollbars) {
      mScrollbars = scroll;
      this.ScrollBars = scroll ? ScrollBars.Vertical : ScrollBars.None;
    }
  }

  protected override void OnTextChanged(EventArgs e) {
    checkForScrollbars();
    base.OnTextChanged(e);
  }

  protected override void OnClientSizeChanged(EventArgs e) {
    checkForScrollbars();
    base.OnClientSizeChanged(e);
  }
}

Andere Tipps

Ich kam in dieser Frage, wenn ich das gleiche Problem lösen wollte.

Der einfachste Weg, es zu tun ist, um System.Windows.Forms.RichTextBox zu ändern. Die Scrollbars-Eigenschaft kann in diesem Fall auf den Standardwert von RichTextBoxScrollBars.Both gelassen werden, was darauf hindeutet „Display sowohl eine horizontale und eine vertikale Bildlaufleiste, wenn nötig.“ Es wäre schön, wenn diese Funktionalität auf TextBox Verfügung gestellt wurde.

ich auch einige Experimente gemacht und festgestellt, dass der vertikale Balken wird immer angezeigt, wenn Sie es zu aktivieren, und der horizontale Balken zeigt immer, solange sie aktiviert und WordWrap == false.

Ich glaube, du bist nicht genau gehen zu bekommen, was Sie hier wollen. Ich glaube jedoch, dass der Benutzer eine bessere Windows-Standardverhalten möchten als die Sie versuchen zu erzwingen. Wenn ich Ihre App verwendet haben, würde ich wahrscheinlich gestört werden, wenn meine Textbox Immobilien plötzlich nur schrumpfte, weil es eine unerwartete Scrollbar empfangen muss, weil ich es zu viel Text gab!

Vielleicht wäre es eine gute Idee, nur Ihre Anwendung Look folgen Fenster zu lassen und fühlen.

Es gibt einen extrem subtilen Fehler in nobugz-Lösung, die in einem Heapbeschädigung führt, aber nur, wenn Sie AppendText verwenden (), um die TextBox zu aktualisieren.

Zur Einstellung der Scrollbars-Eigenschaft von OnTextChanged wird das Win32-Fenster (Handle) verursachen zerstört und neu erstellt werden. Aber OnTextChanged aus den Eingeweiden des Win32-Edit-Control (EditML_InsertText) genannt, die sofort erwartet danach der interne Zustand des Win32-Edit-Control unverändert. Leider, denn das Fenster neu erstellt wird, dass interner Zustand durch die O befreit worden ist, in einer Zugriffsverletzung führt.

Die Moral der Geschichte ist. Nicht verwenden AppendText (), wenn Sie gehen nobugz-Lösung verwenden,

Ich hatte einen gewissen Erfolg mit dem Code unten.

  public partial class MyTextBox : TextBox
  {
    private bool mShowScrollBar = false;

    public MyTextBox()
    {
      InitializeComponent();

      checkForScrollbars();
    }

    private void checkForScrollbars()
    {
      bool showScrollBar = false;
      int padding = (this.BorderStyle == BorderStyle.Fixed3D) ? 14 : 10;

      using (Graphics g = this.CreateGraphics())
      {
        // Calcualte the size of the text area.
        SizeF textArea = g.MeasureString(this.Text,
                                         this.Font,
                                         this.Bounds.Width - padding);

        if (this.Text.EndsWith(Environment.NewLine))
        {
          // Include the height of a trailing new line in the height calculation        
          textArea.Height += g.MeasureString("A", this.Font).Height;
        }

        // Show the vertical ScrollBar if the text area
        // is taller than the control.
        showScrollBar = (Math.Ceiling(textArea.Height) >= (this.Bounds.Height - padding));

        if (showScrollBar != mShowScrollBar)
        {
          mShowScrollBar = showScrollBar;
          this.ScrollBars = showScrollBar ? ScrollBars.Vertical : ScrollBars.None;
        }
      }
    }

    protected override void OnTextChanged(EventArgs e)
    {
      checkForScrollbars();
      base.OnTextChanged(e);
    }

    protected override void OnResize(EventArgs e)
    {
      checkForScrollbars();
      base.OnResize(e);
    }
  }

Was Aidan beschreibt, ist fast genau das UI-Szenario ich bin vor. Da das Textfeld nur gelesen wird, brauche ich nicht zu Textchanged zu reagieren. Und ich würde die Autoscroll Neuberechnung bevorzugt verzögert werden, damit es nicht Dutzende Male pro Sekunde schießen, während ein Fenster verkleinert wird.

Für die meisten UIs, Textfelder mit dem beiden vertikalen und horizontalen Bildlaufleisten sind, gut, böse, also ist ich daran interessiert ist nur in vertikalen Bildlaufleisten hier.

Ich fand auch, dass MeasureString eine Höhe erzeugt, die tatsächlich größer als das, was erforderlich war. Mit Hilfe des PreferredHeight des Textfeldes ohne Rand als die Zeilenhöhe ein besseres Ergebnis gibt.

Die folgende scheint ziemlich gut zu funktionieren, mit oder ohne Rahmen, und es funktioniert mit WordWrap auf.

Rufen Sie einfach an AutoScrollVertically (), wenn Sie es brauchen, und optional angeben recalculateOnResize.

public class TextBoxAutoScroll : TextBox
{
    public void AutoScrollVertically(bool recalculateOnResize = false)
    {
        SuspendLayout();

        if (recalculateOnResize)
        {
            Resize -= OnResize;
            Resize += OnResize;
        }

        float linesHeight = 0;
        var   borderStyle = BorderStyle;

        BorderStyle       = BorderStyle.None;

        int textHeight    = PreferredHeight;

        try
        {
            using (var graphics = CreateGraphics())
            {
                foreach (var text in Lines)
                {
                    var textArea = graphics.MeasureString(text, Font);

                    if (textArea.Width < Width)
                        linesHeight += textHeight;
                    else
                    {
                        var numLines = (float)Math.Ceiling(textArea.Width / Width);

                        linesHeight += textHeight * numLines;
                    }
                }
            }

            if (linesHeight > Height)
                ScrollBars = ScrollBars.Vertical;
            else
                ScrollBars = ScrollBars.None;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
        }
        finally
        {
            BorderStyle = borderStyle;

            ResumeLayout();
        }
    }

    private void OnResize(object sender, EventArgs e)
    {
        m_timerResize.Stop();

        m_timerResize.Tick    -= OnDelayedResize;
        m_timerResize.Tick    += OnDelayedResize;
        m_timerResize.Interval = 475;

        m_timerResize.Start();
    }

    Timer m_timerResize = new Timer();

    private void OnDelayedResize(object sender, EventArgs e)
    {
        m_timerResize.Stop();

        Resize -= OnResize;

        AutoScrollVertically();

        Resize += OnResize;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top