如何截断字符串以适应容器?

时间:2013-07-15 12:26:55

标签: c# winforms truncate

有很多问题(例如:12345)询问如何将字符串截断为所需的字符数量。但我希望将一段文字截断以容纳在容器中。 (IE:按像素不是字符)的宽度裁剪字符串。

如果您使用WPF,这很容易,但在WinForms中却没有那么多......

所以:如何截断字符串以使其适合容器?

2 个答案:

答案 0 :(得分:13)

经过一天的编码后,我找到了一个解决方案,我想与社区分享。

首先:字符串或winforms TextBox没有原生截断函数。如果使用标签,则可以使用AutoEllipsis属性。

  

FYI:省略号是由三个点组成的标点符号。 IE:......

这就是我这样做的原因:

public static class Extensions
{
    /// <summary>
    /// Truncates the TextBox.Text property so it will fit in the TextBox. 
    /// </summary>
    static public void Truncate(this TextBox textBox)
    {
        //Determine direction of truncation
        bool direction = false;
        if (textBox.TextAlign == HorizontalAlignment.Right) direction = true;

        //Get text
        string truncatedText = textBox.Text;

        //Truncate text
        truncatedText = truncatedText.Truncate(textBox.Font, textBox.Width, direction);

        //If text truncated
        if (truncatedText != textBox.Text)
        {
            //Set textBox text
            textBox.Text = truncatedText;

            //After setting the text, the cursor position changes. Here we set the location of the cursor manually.
            //First we determine the position, the default value applies to direction = left.

            //This position is when the cursor needs to be behind the last char. (Example:"…My Text|");
            int position = 0;

            //If the truncation direction is to the right the position should be before the ellipsis
            if (!direction)
            {
                //This position is when the cursor needs to be before the last char (which would be the ellipsis). (Example:"My Text|…");
                position = 1; 
            }

            //Set the cursor position
            textBox.Select(textBox.Text.Length - position, 0);
        }
    }

    /// <summary>
    /// Truncates the string to be smaller than the desired width.
    /// </summary>
    /// <param name="font">The font used to determine the size of the string.</param>
    /// <param name="width">The maximum size the string should be after truncating.</param>
    /// <param name="direction">The direction of the truncation. True for left (…ext), False for right(Tex…).</param>
    static public string Truncate(this string text, Font font, int width, bool direction)
    {
        string truncatedText, returnText;
        int charIndex = 0;
        bool truncated = false;
        //When the user is typing and the truncation happens in a TextChanged event, already typed text could get lost.
        //Example: Imagine that the string "Hello Worl" would truncate if we add 'd'. Depending on the font the output 
        //could be: "Hello Wor…" (notice the 'l' is missing). This is an undesired effect.
        //To prevent this from happening the ellipsis is included in the initial sizecheck.
        //At this point, the direction is not important so we place ellipsis behind the text.
        truncatedText = text + "…";

        //Get the size of the string in pixels.
        SizeF size = MeasureString(truncatedText, font);

        //Do while the string is bigger than the desired width.
        while (size.Width > width)
        {
            //Go to next char
            charIndex++;

            //If the character index is larger than or equal to the length of the text, the truncation is unachievable.
            if (charIndex >= text.Length)
            {
                //Truncation is unachievable!

                //Throw exception so the user knows what's going on.
                throw new IndexOutOfRangeException("The desired width of the string is too small to truncate to.");
            }
            else
            {
                //Truncation is still applicable!

                //Raise the flag, indicating that text is truncated.
                truncated = true;

                //Check which way to text should be truncated to, then remove one char and add an ellipsis.
                if (direction)
                {
                    //Truncate to the left. Add ellipsis and remove from the left.
                    truncatedText = "…" + text.Substring(charIndex);
                }
                else
                {
                    //Truncate to the right. Remove from the right and add the ellipsis.
                    truncatedText = text.Substring(0, text.Length - charIndex) + "…";
                }

                //Measure the string again.
                size = MeasureString(truncatedText, font);
            }
        }

        //If the text got truncated, change the return value to the truncated text.
        if (truncated) returnText = truncatedText;
        else returnText = text;

        //Return the desired text.
        return returnText;
    }

    /// <summary>
    /// Measures the size of this string object.
    /// </summary>
    /// <param name="text">The string that will be measured.</param>
    /// <param name="font">The font that will be used to measure to size of the string.</param>
    /// <returns>A SizeF object containing the height and size of the string.</returns>
    static private SizeF MeasureString(String text, Font font)
    {
        //To measure the string we use the Graphics.MeasureString function, which is a method that can be called from a PaintEventArgs instance.
        //To call the constructor of the PaintEventArgs class, we must pass a Graphics object. We'll use a PictureBox object to achieve this. 
        PictureBox pb = new PictureBox();

        //Create the PaintEventArgs with the correct parameters.
        PaintEventArgs pea = new PaintEventArgs(pb.CreateGraphics(), new System.Drawing.Rectangle());
        pea.Graphics.PageUnit = GraphicsUnit.Pixel;
        pea.Graphics.PageScale = 1;

        //Call the MeasureString method. This methods calculates what the height and width of a string would be, given the specified font.
        SizeF size = pea.Graphics.MeasureString(text, font);

        //Return the SizeF object.
        return size;
    }
}

<强>用法: 这是一个可以复制并粘贴到包含winforms表单的命名空间的类。确保包含“使用System.Drawing;

这个类有两个扩展方法,都叫做Truncate。基本上你现在可以这样做:

public void textBox1_TextChanged(object sender, EventArgs e)
{
    textBox1.Truncate();
}

您现在可以在textBox1中键入内容,如果需要,它会自动截断您的字符串以适应textBox,它将添加省略号。

<强>概述:  该类目前包含3种方法:

  1. 截断(TextBox的扩展名)
  2. 截断(字符串扩展名)
  3. MeasureString
  4.   

    截断(TextBox的扩展名)

         

    此方法将自动截断TextBox.Text属性。截断的方向是TextAlign属性的阻止。 (EG:“左对齐截断...”,“......对齐右对齐”。)请注意:此方法可能需要进行一些修改才能与其他书写系统(如希伯来语或阿拉伯语)一起使用。


      

    截断(字符串扩展名)

         

    要使用此方法,您必须传递两个参数:字体和所需宽度。该字体用于计算字符串的宽度,所需的宽度用作截断后允许的最大宽度。


      

    <强> MeasureString

         

    此方法在代码段中是私有的。因此,如果您想使用它,您必须先将其更改为公共。此方法用于测量字符串的高度和宽度(以像素为单位)。它需要两个参数:要测量的文本和文本的字体。

    我希望我帮助过这个人。也许有另外一种方法可以做到这一点,我找到了Hans Passant的 this 答案,它截断了一个非常令人印象深刻的ToolTipStatusLabel。我的.NET技能远不及Hans Passant,所以我没有设法将代码转换成像TextBox这样的东西......但如果你成功了,或者有其他解决方案,我很乐意看到它! :)

答案 1 :(得分:0)

我测试了Jordy的代码并将结果与​​我的代码进行了比较,没有区别,它们都修剪/截断相当正常,但在某些情况下不太好,可能是由MeasureString()测量的大小不确切。我知道这个代码只是一个简化版本,如果有人关心并使用它,我在这里发布它因为它很短并且我测试过:与Jordy的代码相比,这个代码修剪/截断字符串的准确性没有区别,当然他的代码是某种完整版,支持3种方法。

public static class TextBoxExtension
{
    public static void Trim(this TextBox text){            
        string txt = text.Text;
        if (txt.Length == 0 || text.Width == 0) return;
        int i = txt.Length;            
        while (TextRenderer.MeasureText(txt + "...", text.Font).Width > text.Width)            
        {
            txt = text.Text.Substring(0, --i);
            if (i == 0) break;
        }
        text.Text = txt + "...";
    }
    //You can implement more methods such as receiving a string with font,... and returning the truncated/trimmed version.
 }