问题描述
本周有用户反馈,某软件使用过程中,某模块中有个字段显示有问题,内容显示不全。写了一个示例程序,如下图所示,本来应该显示的内容是红框上方的,但是在真正显示时,内容没有显示全,如红框中的内容所示。
该模块中的信息以比较复杂的卡片方式显示,之前的同事使用C#的GDI+绘制的。原本设计的功能是该字段的显示区域大小和位置是固定的,如果字段字内容过长,则自动减小字体以确保内容能全部显示。该功能已使用了很长时间,之前没有用户反馈过有问题。
原因分析
原来没有看过这部分代码,首先定位问题代码位置。通过一番查找,最后发现绘制字符串的代码大致如下所示(问题示例代码,自己写的用于问题复现)。
从这段代码中看不出问题,绘制字符串及自动缩放字体的代码是在CommonMethod.DrawStringInRectangle函数中。但是这个函数是从其它地方导过来的程序集,没有源代码,无法F11进去调试。
Font initFont = new Font("宋体", 10);
RectangleF rect = new RectangleF(30, 30, 101, 18);
string demoStr = "08Br52Kl8Av9Fn";
e.Graphics.DrawRectangle(Pens.Red, rect.X, rect.Y, rect.Width, rect.Height);
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Near;
CommonMethod.DrawStringInRectangle(e.Graphics, demoStr ,initFont, Brushes.Black, rect, sf);
}
在项目下找到这个函数所在程序集,使用.net反编译工具对这个程序集进行反编译,找到CommonMethod.DrawStringInRectangle的反编译程序,函数的大致逻辑如下所示:
void DrawStringInRectangle(Graphics g,string s,Font font,Brush brush,RectangleF rect,StringFormat sf)
{
SizeF stringSize= g.MeasureString(s, font);
float fontSize = font.Size;
while((stringSize.Width/rect.Width)>((((int)(rect.Height/ stringSize.Height))>=1)?((float)((int)(Height/ stringSize.Height))):((float)1)))
{
fontSize = 0.8f * fontSize;
Font newFont = new Font("Arial", fontSize, FontStyle.Bold);
stringSize = g.MeasureString(s, newFont);
}
Font realFont = new Font("Arial", fontSize, FontStyle.Bold);
g.DrawString(s, realFont, brush, rect, sf);
}
从这段代码函数代码可以看出,判断是否需要缩放字体的代码是下面这句(反编译过来的代码,不知道原来的代码是怎么写的,看着有点晕,可能是判断字符串的宽度和高度是否与绘制位置的矩形大小相匹配)。
(stringSize.Width/rect.Width)>((((int)(rect.Height/ stringSize.Height))>=1)?((float)((int)(Height/ stringSize.Height))):((float)1))
从原始错误及示例问题截图来看,代码应该是没有进入while循环,没有自动缩放字体,直接跳到了循环下面的那条语句。这句导致了绘制问题。刚进入函数时,使用传入的字体(宋体)计算了字符串尺寸,后面判断了不需要缩放字体,结果真正绘制时使用的字体变成了下面语句中的Arial字体,并且还加了粗。两个字体计算出来的字符串大小肯定不一样,Arial字体算出来的要大一些,因此最后在矩形内绘制字符串时没有显示全,没有显示出来的内容应该是换行了。
Font realFont = new Font("Arial", fontSize, FontStyle.Bold);
解决方案
由于函数所在程序集是在外部导入的,没有办法直接修改代码。只有将该函数的代码复制一份到模块代码中,将函数中写死的字体名称替换为函数输入参数中的字体名称,这样就解决问题了。修改后的显示效果如下图所示: