DataGridView TextBox 列 - 当文本较长时显示文本的右侧部分

dan*_*dan 2 .net c# datagridview ellipsis winforms

我有一个DataGridViewWindows 窗体,其中有一个列,我不想将其设置为自动调整大小以适合所有文本。

相反,当文本很长时,我想显示文本的右侧部分。这可能吗?

例如:

  • 单元格的文本是:Some long text

  • 当前宽度仅显示:Some long

  • 我希望它首先显示文本的最右边部分:long text

然后,要查看整个文本,用户可以调整列宽。它与默认行为相反。在默认行为中,它显示文本的左侧部分,并在字符串末尾带有省略号,并且要查看右侧部分,用户必须更改列宽。

谢谢

Rez*_*aei 5

主要思想是处理CellPainting事件DataGridView并使用测量字符串的长度TextRenderer.MeasureText并检查字符串是否比单元格的宽度长,否则在单元格的右侧绘制字符串,否则将其绘制在左侧。

注意:请注意,右对齐文本并不是解决方案。如果使用右对齐,当文本很长时,例如Some long text,它会显示Some lo...,而OP需要文本显示为long text...ng text。该操作需要首先显示文本的正确部分。这与右对齐无关。

void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    if (e.RowIndex >= 0 && e.ColumnIndex == 1)
    {
        var rect = e.CellBounds;
        rect.Inflate(-1, 0);
        e.Paint(e.CellBounds, DataGridViewPaintParts.All
            & ~(DataGridViewPaintParts.ContentForeground));
        var text = string.Format("{0}", e.FormattedValue);
        var size = TextRenderer.MeasureText(text, e.CellStyle.Font);
        var flags = TextFormatFlags.Left;
        if (size.Width > rect.Width)
            flags = TextFormatFlags.Right;
        TextRenderer.DrawText(e.Graphics, text, e.CellStyle.Font,
            rect, e.CellStyle.ForeColor, flags | TextFormatFlags.VerticalCenter);
        e.Handled = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

左图:对于长字符串,您会在单元格中看到字符串的右侧部分。出现短字符串是正常的。

右图:柱子变宽,因此外观正常。

在此输入图像描述 在此输入图像描述

在字符串开头显示省略号

但外观在某种程度上让用户感到困惑,他们不知道哪个单元格有更多文本,哪个单元格没有更多文本。在这种情况下,显示省略号确实很有用,并且根据您的喜好,省略号应该这样显示在字符串的开头:

在此输入图像描述

但问题是绘制一个在字符串开头带有省略号的字符串。

Thomas Polaert 写了一篇有用的文章。我对他共享的类做了一些小更改,以根据字符串、可用宽度、字体和将绘制字符串的图形对象来计算带有起始省略号的字符串。我在这篇文章的末尾发布了更改后的代码。

您需要做的唯一更改是使用省略号计算文本:

var text = string.Format("{0}", e.FormattedValue);
text = AutoEllipsis.Ellipsis.Compact(text, e.Graphics, e.CellBounds.Width,
    e.CellStyle.Font, AutoEllipsis.EllipsisFormat.Start);
Run Code Online (Sandbox Code Playgroud)

其余代码保持不变,结果将是一个开头带有省略号的字符串,尽管默认行为在末尾显示省略号。

自动省略

原始代码依赖于Control. 我对它进行了一些更改以传递Graphics对象,Font我们Width想用它来计算自动省略号:

using System;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace AutoEllipsis
{
    /// <summary>
    /// Specifies ellipsis format and alignment.
    /// </summary>
    [Flags]
    public enum EllipsisFormat
    {
        /// <summary>
        /// Text is not modified.
        /// </summary>
        None = 0,
        /// <summary>
        /// Text is trimmed at the end of the string. An ellipsis (...) is drawn in place of remaining text.
        /// </summary>
        End = 1,
        /// <summary>
        /// Text is trimmed at the begining of the string. An ellipsis (...) is drawn in place of remaining text. 
        /// </summary>
        Start = 2,
        /// <summary>
        /// Text is trimmed in the middle of the string. An ellipsis (...) is drawn in place of remaining text.
        /// </summary>
        Middle = 3,
        /// <summary>
        /// Preserve as much as possible of the drive and filename information. Must be combined with alignment information.
        /// </summary>
        Path = 4,
        /// <summary>
        /// Text is trimmed at a word boundary. Must be combined with alignment information.
        /// </summary>
        Word = 8
    }


    public class Ellipsis
    {
        /// <summary>
        /// String used as a place holder for trimmed text.
        /// </summary>
        public static readonly string EllipsisChars = "...";

        private static Regex prevWord = new Regex(@"\W*\w*$");
        private static Regex nextWord = new Regex(@"\w*\W*");

        /// <summary>
        /// Truncates a text string to fit within a given control width by replacing trimmed text with ellipses. 
        /// </summary>
        /// <param name="text">String to be trimmed.</param>
        /// <param name="ctrl">text must fit within ctrl width.
        /// The ctrl's Font is used to measure the text string.</param>
        /// <param name="options">Format and alignment of ellipsis.</param>
        /// <returns>This function returns text trimmed to the specified witdh.</returns>
        public static string Compact(string text, Graphics g, int width, Font font, EllipsisFormat options)
        {
            if (string.IsNullOrEmpty(text))
                return text;

            // no aligment information
            if ((EllipsisFormat.Middle & options) == 0)
                return text;

            if (g == null)
                throw new ArgumentNullException("g");

            Size s = TextRenderer.MeasureText(g, text, font);

            // control is large enough to display the whole text
            if (s.Width <= width)
                return text;

            string pre = "";
            string mid = text;
            string post = "";

            bool isPath = (EllipsisFormat.Path & options) != 0;

            // split path string into <drive><directory><filename>
            if (isPath)
            {
                pre = Path.GetPathRoot(text);
                mid = Path.GetDirectoryName(text).Substring(pre.Length);
                post = Path.GetFileName(text);
            }

            int len = 0;
            int seg = mid.Length;
            string fit = "";

            // find the longest string that fits into 
            // the control boundaries using bisection method
            while (seg > 1)
            {
                seg -= seg / 2;

                int left = len + seg;
                int right = mid.Length;

                if (left > right)
                    continue;

                if ((EllipsisFormat.Middle & options) == EllipsisFormat.Middle)
                {
                    right -= left / 2;
                    left -= left / 2;
                }
                else if ((EllipsisFormat.Start & options) != 0)
                {
                    right -= left;
                    left = 0;
                }

                // trim at a word boundary using regular expressions
                if ((EllipsisFormat.Word & options) != 0)
                {
                    if ((EllipsisFormat.End & options) != 0)
                    {
                        left -= prevWord.Match(mid, 0, left).Length;
                    }
                    if ((EllipsisFormat.Start & options) != 0)
                    {
                        right += nextWord.Match(mid, right).Length;
                    }
                }

                // build and measure a candidate string with ellipsis
                string tst = mid.Substring(0, left) + EllipsisChars + mid.Substring(right);

                // restore path with <drive> and <filename>
                if (isPath)
                {
                    tst = Path.Combine(Path.Combine(pre, tst), post);
                }
                s = TextRenderer.MeasureText(g, tst, font);

                // candidate string fits into control boundaries, try a longer string
                // stop when seg <= 1
                if (s.Width <= width)
                {
                    len += seg;
                    fit = tst;
                }
            }

            if (len == 0) // string can't fit into control
            {
                // "path" mode is off, just return ellipsis characters
                if (!isPath)
                    return EllipsisChars;

                // <drive> and <directory> are empty, return <filename>
                if (pre.Length == 0 && mid.Length == 0)
                    return post;

                // measure "C:\...\filename.ext"
                fit = Path.Combine(Path.Combine(pre, EllipsisChars), post);

                s = TextRenderer.MeasureText(g, fit, font);

                // if still not fit then return "...\filename.ext"
                if (s.Width > width)
                    fit = Path.Combine(EllipsisChars, post);
            }
            return fit;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)