Come posso mostrare le barre di scorrimento su un Sistema.Windows.Le forme.TextBox solo quando il testo non si adatta?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Per un Sistema.Windows.Le forme.Controllo TextBox Multiline=True, vorrei solo visualizzare le barre di scorrimento quando il testo non si adatta.

Questo è un readonly textbox utilizzato solo per la visualizzazione.È una casella di testo in modo che gli utenti possono copiare il testo.C'è qualcosa di built-in per il supporto auto show di barre di scorrimento?Se no, dovrei usare un altro controllo?O devo gancio TextChanged e verificare manualmente la presenza di overflow (se è così, come dire, se il testo si inserisce?)


Non avendo alcuna fortuna con varie combinazioni di Capo e le Barre di scorrimento impostazioni.Mi piacerebbe avere barre di scorrimento, inizialmente, e ogni essere visualizzati in modo dinamico solo se il testo non si adatta in una data direzione.


@nobugz, grazie, che funziona quando ritorno a Capo automatico è disattivato.Preferisco non disattivare il capo, ma è il minore dei due mali.


@André Neves, buon punto, e vorrei andare in questo modo, se era modificabile dall'utente.Sono d'accordo che la coerenza è la regola fondamentale per UI intuitività.

È stato utile?

Soluzione

Aggiungere una nuova classe per il tuo progetto e incollare il codice riportato di seguito.La compilazione.Rilasciare il nuovo controllo dall'alto della casella degli strumenti nel form.Non è tutto perfetto, ma dovrebbe funzionare per voi.

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);
  }
}

Altri suggerimenti

Mi sono imbattuto in questa domanda, e volevo risolvere lo stesso problema.

Il modo più semplice per farlo è quello di cambiare il Sistema.Windows.Le forme.RichTextBox.La proprietà ScrollBars in questo caso può essere lasciato al valore di default di RichTextBoxScrollBars.Sia che indica "Visualizzazione sia orizzontale e una barra di scorrimento verticale quando è necessario." Sarebbe bello se questa funzionalità sono state fornite su casella di testo.

Ho anche fatto alcuni esperimenti, e ha scoperto che la barra verticale che si mostra sempre se lo si attiva, e la barra orizzontale mostra sempre come è abilitato e WordWrap == false.

Penso che tu non stai andando a ottenere esattamente quello che vuoi qui.Tuttavia, credo che gli utenti vorrebbero meglio di default di Windows comportamento quello che si sta cercando di forzare.Se dovessi usare l'app, io probabilmente avrei disturbato se la mia textbox immobiliare improvvisamente comprimere solo perché ha bisogno di ospitare un imprevisto barra di scorrimento, perché ho dato troppo testo!

Forse sarebbe una buona idea, basta lasciare che la tua applicazione eseguire Windows' look and feel.

C'è un sottile bug in nobugz la soluzione che comporta un danneggiamento dell'heap, ma solo se si sta utilizzando il metodo appendtext() per aggiornare la casella di testo.

Impostando la proprietà ScrollBars da OnTextChanged causa Win32 finestra (manico) per essere distrutto e ricreato.Ma OnTextChanged è chiamato dalle viscere della Win32 controllo di modifica (EditML_InsertText), che, subito dopo, si aspetta che lo stato interno della Win32 controllo di modifica per essere invariato.Purtroppo, dal momento che la finestra è ricreato, che stato interno è stato liberato dal sistema operativo, determinando una violazione di accesso.

Quindi la morale della storia è:non utilizzare il metodo appendtext() se hai intenzione di usare il nobugz soluzione.

Ho avuto un certo successo con il codice riportato di seguito.

  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);
    }
  }

Cosa Aidan viene descritto è quasi esattamente la UI scenario che sto affrontando.Come la casella di testo è di sola lettura, non ho bisogno di rispondere a TextChanged.E io preferisco lo scorrimento automatico il ricalcolo di essere in ritardo, quindi non sparare decine di volte al secondo, mentre una finestra viene ridimensionata.

Per la maggior parte delle Interfacce utente, le caselle di testo con sia in senso verticale e orizzontale delle barre di scorrimento sono, bene, male, così io sono solo interessato a barre di scorrimento verticale qui.

Ho anche scoperto che MeasureString prodotto un altezza che in realtà era la più grande di quello che è stato richiesto.Utilizzando la casella di testo del PreferredHeight senza confine come linea di altezza dà un risultato migliore.

Il seguente sembra funzionare piuttosto bene, con o senza bordo, e funziona con WordWrap su.

Semplicemente chiamata AutoScrollVertically() quando si ha bisogno di esso, e, facoltativamente, specificare 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;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top