在 ASP.NET 中截断文本字符串以适应给定的像素宽度






4.75/5 (8投票s)
如何在 ASP.NET 中截断文本字符串,使其适应以像素为单位指定的给定宽度。
引言
有时在开发 Web 应用程序时,您需要字符串适应一定的像素宽度(通常是容器的宽度,如 div、p、td 等)。 对于链接来说,这尤其常见,因为您有时需要每个链接都适应一行,并且不会换行到下一行(如果链接的文本太宽而无法适应容器的宽度,就会发生这种情况)。 或者用视觉方式表达,您可能有一个看起来像这样的链接
并且想要让它看起来像这样
客户端 vs. 服务器端
这可以在客户端(使用 CSS 或 JavaScript)或服务器端完成。 对于客户端解决方案,CSS3 text-overflow 样式(text-overflow: ellipsis OR text-overflow: ellipsis-word)是最简单也是最标准的方法,但不幸的是,它仍然没有被所有浏览器完全支持。 目前有一些 JavaScript 解决方案可以使用,直到 text-overflow 得到完全支持; 不过,我更喜欢服务器端解决方案,因为它
- 节省带宽(如果文本太长并且需要更长的时间才能下载,尤其是在用户连接速度较慢的情况下,这对用户体验也有好处)。
- 保证在所有浏览器(包括较旧的浏览器)中都能正常工作,无论它们是否启用了 JavaScript,甚至是否支持它。
代码
我将截断所需的代码包装到一个类 `TextTruncator` 中,您可以在此处看到(也包含在下载文件中,以及一个演示项目)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace aspnet_TextTruncator
{
public static class TextTruncator
{
// Private Properties
//
private static Dictionary<string, int[]> _fontWidthDic;
private static Dictionary<string, int[]> FontWidthDic
{
get
{
if (_fontWidthDic == null)
{
_fontWidthDic = new Dictionary<string, int[]>();
}
return _fontWidthDic;
}
}
//
// Public Methods
//
public static string TruncateText(string text, int textMaxWidth,
string fontName, int fontSizeInPixels)
{
return TruncateText(text, textMaxWidth, fontName,
fontSizeInPixels, false);
}
public static string TruncateText(string text, int textMaxWidth,
string fontName, int fontSizeInPixels, bool isFontBold)
{
if (string.IsNullOrEmpty(text))
return text;
// Check
//
if (textMaxWidth < 1 ||
string.IsNullOrEmpty(fontName) ||
fontSizeInPixels < 1)
{
throw new ArgumentException();
}
int[] fontWidthArray = GetFontWidthArray(fontName,
fontSizeInPixels, isFontBold);
int ellipsisWidth = fontWidthArray['.'] * 3;
int totalCharCount = text.Length;
int textWidth = 0;
int charIndex = 0;
for (int i = 0; i < totalCharCount; i++)
{
textWidth += fontWidthArray[text[i]];
if (textWidth > textMaxWidth)
{
return text.Substring(0, charIndex) + "...";
}
else if (textWidth + ellipsisWidth <= textMaxWidth)
{
charIndex = i;
}
}
return text;
}
//
// Private Methods
//
private static int[] GetFontWidthArray(string fontName,
int fontSizeInPixels, bool isFontBold)
{
string fontEntryName = fontName.ToLower() + "_" +
fontSizeInPixels.ToString() + "px" +
(isFontBold ? "_bold" : "");
int[] fontWidthArray;
if (!FontWidthDic.TryGetValue(fontEntryName, out fontWidthArray))
{
fontWidthArray = CreateFontWidthArray(new Font(fontName,
fontSizeInPixels, isFontBold ? FontStyle.Bold :
FontStyle.Regular, GraphicsUnit.Pixel));
FontWidthDic[fontEntryName] = fontWidthArray;
}
return fontWidthArray;
}
private static int[] CreateFontWidthArray(Font font)
{
int[] fontWidthArray = new int[256];
for (int i = 32; i < 256; i++)
{
char c = (char)i;
fontWidthArray[i] =
IsIllegalCharacter(c, false) ? 0 : GetCharWidth(c, font);
}
return fontWidthArray;
}
private static int GetCharWidth(char c, Font font)
{
// Note1: For typography related reasons,
// TextRenderer.MeasureText() doesn't return the correct
// width of the character in pixels, hence the need
// to use this hack (with the '<' & '>'
// characters and the subtraction). Note that <'
// and '>' were chosen randomly, other characters
// can be used.
//
// Note2: As the TextRenderer class is intended
// to be used with Windows Forms Applications, it has a
// special use for the ampersand character (used for Mnemonics).
// Therefore, we need to check for the
// ampersand character and replace it with '&&'
// to escape it (TextRenderer.MeasureText() will treat
// it as one ampersand character)
//
return
TextRenderer.MeasureText("<" + (c == '&' ? "&&" :
c.ToString()) + ">", font).Width -
TextRenderer.MeasureText("<>", font).Width;
}
private static bool ContainsIllegalCharacters(string text,
bool excludeLineBreaks)
{
if (!string.IsNullOrEmpty(text))
{
foreach (char c in text)
{
if (IsIllegalCharacter(c, excludeLineBreaks))
return true;
}
}
return false;
}
private static bool IsIllegalCharacter(char c, bool excludeLineBreaks)
{
// See the Windows-1252 encoding
// (we use ISO-8859-1, but all browsers, or at least
// IE, FF, Opera, Chrome and Safari,
// interpret ISO-8859-1 as Windows-1252).
// For more information,
// see http://en.wikipedia.org/wiki/ISO/
// IEC_8859-1#ISO-8859-1_and_Windows-1252_confusion
//
return
(c < 32 && (!excludeLineBreaks || c != '\n')) ||
c > 255 ||
c == 127 ||
c == 129 ||
c == 141 ||
c == 143 ||
c == 144 ||
c == 157;
}
}
}
Using the Code
该类只有一个重载的公共方法 `TruncateText()`,您应该调用该方法来截断文本。
使用示例
在您的 * .aspx * 页面中
<%= TextTruncator.TruncateText("Some text that will " +
"likely come from your database, an XML file, or another source",
300, "Verdana", 12) %>
关于代码的说明
- 为了测量文本宽度,我使用 `TextRenderer.MeasureText()`,它实际上是为 Windows Forms 应用程序设计的,但仍然可以与 ASP.NET 一起使用。 出于性能原因,我缓存了字母的宽度,这样我就不必再次调用 `TextRenderer.MeasureText()`。 这样做是有充分理由的,因为 `TextRenderer.MeasureText()` 可能会非常慢(在我的测试中,这个改变将一百万个字符串的截断时间从 8 分钟缩短到 2 秒!)。
- 因为我选择缓存字母的宽度,所以我不得不将字符集限制为 ISO-8859-1(对于基于拉丁语的语言),这对于我自己的目的来说效果很好。 如果您需要此代码与 Unicode 一起使用,您需要删除字母宽度缓存机制,并在每次要测量文本宽度时调用 `TextRenderer.MeasureText()`(这会稍微减慢速度,但除非您计划在非常高的流量网站和速度不够快的机器上使用它,否则应该不会很明显;您需要进行自己的测试才能确定)。 您还可以更改字母宽度缓存机制以使用字典而不是数组,并且仅在实际使用字母时才将它们添加到字典中。
- 您可能会注意到,在 `TruncateText()` 中,我传递字体名称、像素大小以及是否粗体,而不是传递字体对象。 我这样做是为了方便,但如果您想传递 `Font` 对象,这应该很容易更改(可能还要使用其他字体样式,如斜体,但您还需要更改字母宽度的缓存机制)。
- 您需要在项目中添加对 `System.Windows.Forms` 和 `System.Drawing` 程序集的引用,以便此代码能够正常工作。
关于 Web 浏览器的注意事项
在网络上的许多讨论中,您会读到您不应该依赖文本宽度(以像素为单位),因为不能保证不同的浏览器会以相同的方式显示文本(即,文本的宽度可能因浏览器而异)。 虽然不能保证,但在所有流行的浏览器(以及不太流行的浏览器)中的实际测试中,我发现在所有浏览器中,浏览器中显示的文本的宽度从未超过我在 `TruncateText()` 中为文本指定的最大宽度; 有时它略小(仅几个像素 - 那是在旧版本的 Safari on Windows 中),但同样,它从未更大。 如果您像我一样偏执,请始终使用“安全边距”。 例如,如果文本容器(div、p、td 或任何元素)的宽度为 500px,则使最大宽度(传递给 `TruncateText()`)减少 10px 或 20px,即 490 或 480。