DataGridView 文本框列 - 当文本很长时显示文本的右侧部分
DataGridView TextBox Column - Show Right Part of Text when Text is Long
我在 windows 表单中有一个 DataGridView
,其中有一列我不想将其设置为自动调整大小以适合所有文本。
相反,当文本很长时,我想显示文本的右侧部分。这可能吗?
例如:
单元格的文本是:Some long text
当前宽度只能显示:Some long
我希望它首先显示文本的最右边部分:long text
然后要查看整个文本,用户可以调整列宽。它与默认行为相反。在默认行为中,它在字符串末尾显示带有省略号的文本左侧部分,要查看右侧部分,用户必须更改列宽。
谢谢
主要思想是处理DataGridView
的CellPainting
事件并使用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;
}
}
左图:对于长字符串,您会在单元格中看到字符串的右侧部分。短字符串出现是正常的。
右图:柱子变宽了,外观正常。
在字符串开头显示省略号
但外观在某种程度上让用户感到困惑,他们不知道哪个单元格有更多文本,哪个单元格没有更多文本。在这种情况下,显示省略号非常有用,根据您的喜好,省略号应该以这种方式显示在字符串的开头:
但问题是在字符串的开头绘制一个带省略号的字符串。
Thomas Polaert 写了一篇有用的 article。我对他共享的 class 进行了一些小改动,以根据字符串、可用宽度、字体和绘制字符串的图形对象计算带有开始省略号的字符串。我已经 post 在这个 post 末尾更改了代码。
您需要做的唯一更改是用省略号计算文本:
var text = string.Format("{0}", e.FormattedValue);
text = AutoEllipsis.Ellipsis.Compact(text, e.Graphics, e.CellBounds.Width,
e.CellStyle.Font, AutoEllipsis.EllipsisFormat.Start);
其余代码保持不变,结果将是一个开头带有省略号的字符串,尽管默认行为在末尾显示省略号。
自动省略号
原代码依赖一个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;
}
}
}
我在 windows 表单中有一个 DataGridView
,其中有一列我不想将其设置为自动调整大小以适合所有文本。
相反,当文本很长时,我想显示文本的右侧部分。这可能吗?
例如:
单元格的文本是:
Some long text
当前宽度只能显示:
Some long
我希望它首先显示文本的最右边部分:
long text
然后要查看整个文本,用户可以调整列宽。它与默认行为相反。在默认行为中,它在字符串末尾显示带有省略号的文本左侧部分,要查看右侧部分,用户必须更改列宽。
谢谢
主要思想是处理DataGridView
的CellPainting
事件并使用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;
}
}
左图:对于长字符串,您会在单元格中看到字符串的右侧部分。短字符串出现是正常的。
右图:柱子变宽了,外观正常。
在字符串开头显示省略号
但外观在某种程度上让用户感到困惑,他们不知道哪个单元格有更多文本,哪个单元格没有更多文本。在这种情况下,显示省略号非常有用,根据您的喜好,省略号应该以这种方式显示在字符串的开头:
但问题是在字符串的开头绘制一个带省略号的字符串。
Thomas Polaert 写了一篇有用的 article。我对他共享的 class 进行了一些小改动,以根据字符串、可用宽度、字体和绘制字符串的图形对象计算带有开始省略号的字符串。我已经 post 在这个 post 末尾更改了代码。
您需要做的唯一更改是用省略号计算文本:
var text = string.Format("{0}", e.FormattedValue);
text = AutoEllipsis.Ellipsis.Compact(text, e.Graphics, e.CellBounds.Width,
e.CellStyle.Font, AutoEllipsis.EllipsisFormat.Start);
其余代码保持不变,结果将是一个开头带有省略号的字符串,尽管默认行为在末尾显示省略号。
自动省略号
原代码依赖一个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;
}
}
}