在Web开发中,有时需要确保文本能够在特定的像素宽度内显示,特别是当文本作为链接时,希望所有的链接都能在一行内显示,而不是换行。如果链接文本太长而无法适应容器的宽度,就会发生换行。为了解决这个问题,可以采用客户端或服务器端的解决方案。客户端解决方案通常使用CSS3的text-overflow属性,但遗憾的是,并非所有浏览器都完全支持这一属性。在text-overflow属性得到完全支持之前,可以使用一些JavaScript解决方案。然而,更倾向于服务器端解决方案,因为它可以节省带宽,并且可以保证在所有浏览器中都能正常工作,无论用户是否启用了JavaScript。
服务器端解决方案的优势在于它能够节省带宽,并且能够保证在所有浏览器中都能正常工作,包括旧版本的浏览器。为了实现文本截断,可以将所需的代码封装到一个名为TextTruncator的类中。
TextTruncator类包含一个名为TruncateText的公共方法,该方法用于截断文本。以下是C#语言的实现示例:
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 static Dictionary _fontWidthDic;
public static Dictionary FontWidthDic
{
get
{
if (_fontWidthDic == null)
{
_fontWidthDic = new Dictionary();
}
return _fontWidthDic;
}
}
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;
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 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)
{
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)
{
return (c < 32 && (!excludeLineBreaks || c != '\n')) || c > 255 || c == 127 || c == 129 || c == 141 || c == 143 || c == 144 || c == 157;
}
}
}
在ASP.NET页面中使用TextTruncator类的方法如下:
<%= 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()方法。由于选择了缓存字母宽度,将字符集限制为ISO-8859-1(适用于基于拉丁语的语言),这对来说已经足够了。如果需要这段代码支持Unicode,需要移除字母宽度缓存机制,并在每次想要测量文本宽度时调用TextRenderer.MeasureText()方法(这会稍微减慢速度,但除非计划在流量非常高的网站和不够快的机器上使用它,否则应该不会有明显的影响;需要自己进行测试以确保)。也可以将字母宽度缓存机制改为使用字典而不是数组,并在实际使用字母时才将它们添加到字典中。