Автоматически масштабировать шрифт в элементе управления TextBox, чтобы он был максимально большим и по-прежнему помещался в пределах границ текстовой области. - PullRequest
4 голосов
/ 27 апреля 2009

Мне нужен элемент управления TextBox или какой-либо другой тип многострочной метки, который автоматически подстраивает размер шрифта, чтобы сделать его максимально большим, и в то же время помещать все сообщение в пределах границ текстовой области.

Я хотел узнать, реализовал ли кто-нибудь пользовательский элемент управления, подобный этому, прежде чем разрабатывать свой собственный.

Пример приложения: есть TextBox, который будет иметь половину площади в форме окна. Когда приходит сообщение, длина которого составляет примерно 100-500 символов, он помещает весь текст в элемент управления и устанавливает шрифт как можно большего размера. Реализация, которая использует Mono Поддерживаемые библиотеки .NET, будет плюсом.

Если вы знаете, что уже реализован элемент управления ... Если кто-то знает, как проверить, полностью ли вписывается данный текст в текстовую область, что было бы полезно, если бы я развернул свой собственный элемент управления.

Редактировать: я закончил писать расширение для RichTextBox. Я опубликую свой код вскоре после того, как я проверю, что все перегибы проработаны.

Ответы [ 4 ]

4 голосов
/ 12 марта 2010

Мне пришлось решить ту же основную проблему. Приведенные выше итерационные решения были очень медленными. Итак, я изменил его следующим образом. Та же идея Просто использует вычисленные отношения вместо итеративного. Возможно, не совсем так точно. Но гораздо быстрее.

Для однократной необходимости я просто накинул обработчик событий на ярлык с моим текстом.

    private void PromptLabel_TextChanged(object sender, System.EventArgs e)
    {
        if (PromptLabel.Text.Length == 0)
        {
            return;
        }

        float height = PromptLabel.Height * 0.99f;
        float width = PromptLabel.Width * 0.99f;

        PromptLabel.SuspendLayout();

        Font tryFont = PromptLabel.Font;
        Size tempSize = TextRenderer.MeasureText(PromptLabel.Text, tryFont);

        float heightRatio = height / tempSize.Height;
        float widthRatio = width / tempSize.Width;

        tryFont = new Font(tryFont.FontFamily, tryFont.Size * Math.Min(widthRatio, heightRatio), tryFont.Style);

        PromptLabel.Font = tryFont;
        PromptLabel.ResumeLayout();
    }
3 голосов
/ 13 мая 2009

Решением, которое я придумал, было написание элемента управления, расширяющего стандартный элемент управления RichTextBox.

Используйте расширенный элемент управления так же, как обычный элемент управления RichTextBox со следующими улучшениями:

  • Вызвать метод ScaleFontToFit () после изменения размера или изменения текста.
  • Поле «Выравнивание по горизонтали» можно использовать для выравнивания текста по центру.
  • Атрибуты Font, установленные в конструкторе, будут использоваться для всего региона. Нельзя смешивать шрифты, так как они будут изменены после вызова метода ScaleFontToFit.

Этот элемент управления объединяет несколько методов, чтобы определить, находится ли текст в пределах его границ. Если текстовая область многострочная, она определяет, видны ли полосы прокрутки. Я нашел умный способ определить, являются ли полосы прокрутки видимыми, не требуя каких-либо вызовов winapi, используя умную технику, которую я нашел в одном из сообщений Патрика Смаккии. . Когда многострочная неверна, вертикальные полосы прокрутки никогда не появляются, поэтому вам нужно использовать другую технику, которая основана на визуализации текста с использованием объекта Graphics. Техника графического рендеринга не подходит для многострочных блоков, потому что вам придется учитывать перенос слов.

Вот несколько фрагментов, которые показывают, как это работает (ссылка на исходный код приведена ниже). Этот код можно легко использовать для расширения других элементов управления.

    /// <summary>
    /// Sets the font size so the text is as large as possible while still fitting in the text
    /// area with out any scrollbars.
    /// </summary>
    public void ScaleFontToFit()
    {
        int fontSize = 10;
        const int incrementDelta = 5; // amount to increase font by each loop iter.
        const int decrementDelta = 1; // amount to decrease to fine tune.

        this.SuspendLayout();

        // First we set the font size to the minimum.  We assume at the minimum size no scrollbars will be visible.
        SetFontSize(MinimumFontSize);

        // Next, we increment font size until it doesn't fit (or max font size is reached).
        for (fontSize = MinFontSize; fontSize < MaxFontSize; fontSize += incrementDelta)
        {
            SetFontSize(fontSize);

            if (!DoesTextFit())
            {
                //Console.WriteLine("Text Doesn't fit at fontsize = " + fontSize);
                break;
            }
        }

        // Finally, we keep decreasing the font size until it fits again.
        for (; fontSize > MinFontSize && !DoesTextFit(); fontSize -= decrementDelta)
        {
            SetFontSize(fontSize);
        }

        this.ResumeLayout();
    }

    #region Private Methods
    private bool VScrollVisible
    {
        get
        {
            Rectangle clientRectangle = this.ClientRectangle;
            Size size = this.Size;
            return (size.Width - clientRectangle.Width) >= SystemInformation.VerticalScrollBarWidth;
        }
    }

    /**
     * returns true when the Text no longer fits in the bounds of this control without scrollbars.
    */
    private bool DoesTextFit()
    {
            if (VScrollVisible)
            {
                //Console.WriteLine("#1 Vscroll is visible");
                return false;
            }

            // Special logic to handle the single line case... When multiline is false, we cannot rely on scrollbars so alternate methods.
            if (this.Multiline == false)
            {
                Graphics graphics = this.CreateGraphics();
                Size stringSize = graphics.MeasureString(this.Text, this.SelectionFont).ToSize();

                //Console.WriteLine("String Width/Height: " + stringSize.Width + " " + stringSize.Height + "form... " + this.Width + " " + this.Height);

                if (stringSize.Width > this.Width)
                {
                    //Console.WriteLine("#2 Text Width is too big");
                    return false;
                }

                if (stringSize.Height > this.Height)
                {
                    //Console.WriteLine("#3 Text Height is too big");
                    return false;
                }

                if (this.Lines.Length > 1)
                {
                    //Console.WriteLine("#4 " + this.Lines[0] + " (2): " + this.Lines[1]); // I believe this condition could be removed.
                    return false;
                }
            }

            return true;
    }

    private void SetFontSize(int pFontSize)
    {
        SetFontSize((float)pFontSize);
    }

    private void SetFontSize(float pFontSize)
    {
        this.SelectAll();
        this.SelectionFont = new Font(this.SelectionFont.FontFamily, pFontSize, this.SelectionFont.Style);
        this.SelectionAlignment = HorizontalAlignment;
        this.Select(0, 0);
    }
    #endregion

ScaleFontToFit можно оптимизировать для повышения производительности, но я оставил это простым, чтобы его было легко понять.

Загрузите последний исходный код здесь. Я все еще активно работаю над проектом, для которого я разработал этот элемент управления, так что, вероятно, я добавлю несколько других функций и улучшений в ближайшем будущем. Итак, проверьте сайт на наличие последнего кода.

Моя цель - заставить этот элемент управления работать на Mac с использованием платформы Mono.

3 голосов
/ 27 апреля 2009

Я не видел существующего элемента управления, чтобы сделать это, но вы можете сделать это сложным путем, используя RichTextBox и метод MeasureText TextRenderer и многократно изменяя размер шрифта. Это неэффективно, но работает.

Эта функция является обработчиком событий для события TextChanged в RichTextBox.

Я заметил проблему:

При наборе текстовое поле будет прокручиваться до текущей каретки, даже если полосы прокрутки отключены. Это может привести к тому, что верхняя линия или левая сторона будут срезаны, пока вы не вернетесь назад или влево с помощью клавиш со стрелками. Размер вычисляется правильно, если вы можете получить верхнюю строку для отображения в верхней части текстового поля. Я включил код прокрутки, который помогает иногда (но не всегда).

Этот код предполагает, что перенос слов отключен. Может потребоваться модификация, если включена перенос слов.


код:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint wMsg, int wParam, uint lParam);

private static uint EM_LINEINDEX = 0xbb;

private void richTextBox1_TextChanged(object sender, EventArgs e)
{
    // If there's no text, return
    if (richTextBox1.TextLength == 0) return;

    // Get height and width, we'll be using these repeatedly
    int height = richTextBox1.Height;
    int width = richTextBox1.Width;

    // Suspend layout while we mess with stuff
    richTextBox1.SuspendLayout();

    Font tryFont = richTextBox1.Font;
    Size tempSize = TextRenderer.MeasureText( richTextBox1.Text, richTextBox1.Font);

    // Make sure it isn't too small first
    while (tempSize.Height < height || tempSize.Width < width)
    {
        tryFont = new Font(tryFont.FontFamily, tryFont.Size + 0.1f, tryFont.Style);
        tempSize = TextRenderer.MeasureText(richTextBox1.Text, tryFont);
    }

    // Now make sure it isn't too big
    while (tempSize.Height > height || tempSize.Width > width)
    {
        tryFont = new Font(tryFont.FontFamily, tryFont.Size - 0.1f, tryFont.Style);
        tempSize = TextRenderer.MeasureText(richTextBox1.Text, tryFont);
    }

    // Swap the font
    richTextBox1.Font = tryFont;

    // Resume layout
    richTextBox1.ResumeLayout();

    // Scroll to top (hopefully)
    richTextBox1.ScrollToCaret();
    SendMessage(richTextBox1.Handle, EM_LINEINDEX, -1, 0);
}
0 голосов
/ 27 апреля 2017

У меня было похожее требование для текстового поля на панели в размещенном окне окна. (Я вставил панель в существующую форму). Когда размер панели изменится (в моем случае), текст изменится в соответствии с размером окна. Код

parentObject.SizeChanged += (sender, args) =>
{
   if (textBox1.Text.Length > 0)
   {
       int maxSize = 100;

       // Make a Graphics object to measure the text.
       using (Graphics gr = textBox1.CreateGraphics())
       {
            for (int i = 1; i <= maxSize; i++)
            {
                 using (var test_font = new Font(textBox1.Font.FontFamily, i))
                   {
                        // See how much space the text would
                        // need, specifying a maximum width.
                        SizeF text_size =
                                    TextRenderer.MeasureText(
                                        textBox1.Text,
                                        test_font,
                                        new Size(textBox1.Width, int.MaxValue),
                                        TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);

                                try
                                {
                                    if (text_size.Height > textBox1.Height)
                                    {
                                        maxSize = i - 1;
                                        break;
                                    }
                                }
                                catch (System.ComponentModel.Win32Exception)
                                {
                                    // this sometimes throws a "failure to create window handle" error.
                                    // This might happen if the TextBox is invisible and/or
                                    // too small to display a toolbar.
                                    // do whatever here, add/delete, whatever, maybe set to default font size?
                                    maxSize = (int) textBox1.Font.Size;
                                }
                            }
                        }
                    }

                    // Use that font size.
                    textBox1.Font = new Font(textBox1.Font.FontFamily, maxSize);

                }
            };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...