应用开发中经常会需要根据显示区域的宽度来决定能显示字符的个数,但受字符不同、字体不同、字号不同的影响,这个个数又无法固定。怎么办呢?这就需要我们去测量每个字符在实际显示中所占的宽度,然后和可显示宽度做比较来判断最终显示的字符。
那么如何测量字符的显示宽度呢?
Win API中提供了GetTextMetrics方法。
WinForm中提供了Graphics.MeasureString,通过Reflector能看到MeasureString最终也是通过调用gdiplus.dll来完成的:
[DllImport("gdiplus.dll", CharSet=CharSet.Unicode, SetLastError=true, ExactSpelling=true)]internalstaticexternintGdipMeasureString(HandleRef graphics,stringtextString,intlength, HandleRef font,refGPRECTF layoutRect, HandleRef stringFormat, [In, Out]refGPRECTF boundingBox,outintcodepointsFitted,outintlinesFilled);
但WPF中既没有HDC,也没有Graphics,只有一个DrawingContext.显然想用API来搞定是不可能的了。
不过还好WPF中很多涉及字符显示的控件,像TextBlock、FormattedText都提供了Trimming属性,只需要将Trimming属性,设置为:Trimming.CharacterEllipsis或Trimming.WordEllipsis,就可以实现自动根据最大可显示宽度裁剪你的字符串并附加上"..."。
有些人估计看到这儿估计想骂我了:“大哥,你啰啰嗦嗦扯了这么多,不会就是想告诉我们WPF中TextBlock等控件具有自动裁剪功能吧”.
好吧,要不是因为TextBlock并不能完成我的需求,估计只能写到这了。
我的字符串:"xxxxxxxxxxx(123)",我希望的裁剪效果是:"xxxxx...(123)",而TextBlock只能整成这个样子:"xxxxxxxx..."。
不过既然TextBlock实现了裁剪的功能,咱们又有强大的Reflector,那接下来的任务是不是很简单了。
事实告诉我,一点也不简单,靠, MS 那帮龟孙子们写的代码可读性太差了。云里雾里最后终于找到一个叫"GlyphRun”的类,查MSDN:
GlyphRun Class
Represents a sequence of glyphs from a single face of a single font at a single size, and with a single rendering style.
而且还给配了一副很牛X的图:
感觉离目标不远了,接着就发现了GlyphTypeface 类,GlyphTypeface提供了两个集合属性:
CharacterToGlyphMap,获取某个字符在“font CMAP table” 中对应的glyph索引
AdvanceWidths,根据glyph索引,获取某个字符的轮廓(glyph)宽度,实际宽度还要乘以字符大小。
原来每个字符的轮廓宽度早在“font CMAP table"约定好了,至于"font CMAP table”是什么,感兴趣的朋友自己去Google吧