【问题标题】:How to truncate a string to fit in a container?如何截断字符串以适合容器?
【发布时间】:2013-07-15 12:26:55
【问题描述】:

有很多问题(例如:12345)询问如何将字符串截断为所需数量的字符。但我想要截断一段文本以适合容器。 (即:按像素的宽度裁剪字符串,不是字符)。

如果您使用 WPF,这很容易,但在 WinForms 中就不那么简单了...

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

【问题讨论】:

    标签: c# winforms truncate


    【解决方案1】:

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

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

    仅供参考:省略号是由三个点组成的标点符号。即:……

    这就是我做这个的原因:

    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 表单的命名空间中复制和粘贴。确保包含“using System.Drawing;

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

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

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

    概述: 该类目前包含 3 个方法:

    1. 截断(TextBox 的扩展)
    2. 截断(字符串的扩展)
    3. 测量字符串

    截断(文本框的扩展)

    此方法将自动截断 TextBox.Text 属性。截断的方向由 TextAlign 属性决定。 (例如:“左对齐截断...”、“右对齐... p>


    截断(字符串的扩展)

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


    测量字符串

    此方法在代码 sn-p 中是私有的。所以如果你想使用它,你必须先把它改成public。此方法用于测量字符串的高度和宽度(以像素为单位)。它需要两个参数:要测量的文本和文本的字体。

    我希望我在这方面对某人有所帮助。也许还有其他方法可以做到这一点,我找到了 Hans Passant 的 this 答案,它截断了 ToolTipStatusLabel,这非常令人印象深刻。我的 .NET 技能与 Hans Passant 相去甚远,所以我还没有设法将该代码转换为使用 TextBox 之类的东西……但如果你成功了,或者有其他解决方案,我很乐意看到它! :)

    【讨论】:

    • +1 获取有关您的解决方案的一些令人难以置信的文档。但是,这种方法实际上改变了TextBox 的文本。这当然可以通过Tag 等其他属性来管理,但汉斯的回答只是覆盖了Paint 过程。这使得Text 单独存在,但为用户提供了文本被截断的错觉
    • @MichaelPerrenoud 这确实值得注意。也是我提到 Hans Passants 回答的原因。它只是更优雅:)
    • 有一个注意事项,经过一些研究,您不能真正自定义在文本框上绘制任何内容。所以我会说你需要什么,你已经尽力了。很棒的工作!
    • @MichaelPerrenoud 你让我很开心 :)
    【解决方案2】:

    我测试了 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.
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-26
      • 2014-12-04
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多