كيف يمكنني إظهار أشرطة التمرير على System.Windows.Forms.TextBox فقط عندما لا يكون النص مناسبًا؟

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

  •  09-06-2019
  •  | 
  •  

سؤال

بالنسبة إلى System.Windows.Forms.TextBox مع Multiline=True، أرغب في عرض أشرطة التمرير فقط عندما لا يكون النص مناسبًا.

هذا مربع نص للقراءة فقط يستخدم للعرض فقط.إنه TextBox حتى يتمكن المستخدمون من نسخ النص.هل هناك أي شيء مدمج لدعم العرض التلقائي لأشرطة التمرير؟إذا لم يكن الأمر كذلك، فهل يجب علي استخدام عنصر تحكم مختلف؟أو هل أحتاج إلى ربط TextChanged والتحقق يدويًا من تجاوز السعة (إذا كان الأمر كذلك، فكيف أعرف ما إذا كان النص مناسبًا؟)


لم يكن لديك أي حظ مع مجموعات مختلفة من إعدادات WordWrap وScrollbars.أرغب في عدم وجود أشرطة تمرير في البداية وأن يظهر كل منها ديناميكيًا فقط إذا كان النص لا يتناسب مع الاتجاه المحدد.


@nobugz، شكرًا، يعمل هذا عند تعطيل WordWrap.أفضل عدم تعطيل التفاف الكلمات، ولكنه أهون الشرين.


@ أندريه نيفيس، نقطة جيدة، وسأذهب بهذه الطريقة إذا كانت قابلة للتعديل من قبل المستخدم.أوافق على أن الاتساق هو القاعدة الأساسية لبديهية واجهة المستخدم.

هل كانت مفيدة؟

المحلول

أضف فصلًا جديدًا إلى مشروعك والصق الكود الموضح أدناه.تجميع.قم بإسقاط عنصر التحكم الجديد من أعلى مربع الأدوات في النموذج الخاص بك.إنها ليست مثالية تمامًا ولكن يجب أن تعمل من أجلك.

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

نصائح أخرى

لقد صادفت هذا السؤال عندما أردت حل نفس المشكلة.

أسهل طريقة للقيام بذلك هي التغيير إلى System.Windows.Forms.RichTextBox.يمكن ترك خاصية Scrollbars في هذه الحالة إلى القيمة الافتراضية لـ RichTextBoxScrollbars.both ، والتي تشير إلى "عرض شريط تمرير أفقي وعمودي عند الحاجة". سيكون من الرائع توفير هذه الوظيفة على مربع النص.

لقد قمت أيضًا ببعض التجارب، ووجدت أن الشريط الرأسي سيظهر دائمًا إذا قمت بتمكينه، وسيظهر الشريط الأفقي دائمًا طالما تم تمكينه و WordWrap == false.

أعتقد أنك لن تحصل على ما تريده بالضبط هنا.ومع ذلك، أعتقد أن المستخدمين يرغبون في الحصول على سلوك افتراضي أفضل لنظام التشغيل Windows من السلوك الذي تحاول فرضه.إذا كنت أستخدم تطبيقك، فمن المحتمل أن أشعر بالانزعاج إذا تقلصت مساحة صندوق النص الخاص بي فجأة لمجرد أنه يحتاج إلى استيعاب شريط تمرير غير متوقع لأنني أعطيته الكثير من النص!

ربما تكون فكرة جيدة أن تسمح لتطبيقك بتتبع شكل Windows ومظهره.

يوجد خطأ دقيق للغاية في حل nobugz يؤدي إلى تلف الكومة، ولكن فقط إذا كنت تستخدم AppendText() لتحديث TextBox.

سيؤدي تعيين خاصية ScrollBars من OnTextChanged إلى تدمير نافذة Win32 (المقبض) وإعادة إنشائها.ولكن يتم استدعاء OnTextChanged من الجزء الداخلي لعنصر تحكم التحرير Win32 (EditML_InsertText)، والذي يتوقع بعد ذلك مباشرة أن الحالة الداخلية لعنصر تحكم التحرير Win32 هذا لن تتغير.لسوء الحظ، منذ إعادة إنشاء النافذة، تم تحرير تلك الحالة الداخلية بواسطة نظام التشغيل، مما أدى إلى انتهاك الوصول.

إذن المغزى من القصة هو:لا تستخدم AppendText() إذا كنت ستستخدم حل nobugz.

لقد حققت بعض النجاح مع الكود أدناه.

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

ما يصفه إيدان هو تقريبًا سيناريو واجهة المستخدم الذي أواجهه.نظرًا لأن مربع النص للقراءة فقط، فلا أحتاج إليه للرد على TextChanged.وأفضّل تأجيل إعادة حساب التمرير التلقائي حتى لا يتم إطلاقها عشرات المرات في الثانية أثناء تغيير حجم النافذة.

بالنسبة لمعظم واجهات المستخدم، تعد مربعات النص التي تحتوي على أشرطة التمرير الرأسية والأفقية شريرة، لذلك أنا مهتم فقط بأشرطة التمرير العمودية هنا.

لقد وجدت أيضًا أن MeasureString أنتج ارتفاعًا كان في الواقع أكبر مما هو مطلوب.استخدام PreferredHeight لمربع النص بدون حدود حيث أن ارتفاع الخط يعطي نتيجة أفضل.

يبدو أن ما يلي يعمل بشكل جيد، مع أو بدون حدود، ويعمل مع WordWrap.

ما عليك سوى الاتصال بـ AutoScrollVertically() عندما تحتاج إليه، وتحديد 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;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top