最近在做Winform的项目,发现Winform和WebForm里相同名称的控件还是真是有区别。比如Label,它的大小Size有两种模式,自动大小(只有一行,宽度根据内容调整),还有一种是自定义宽和高。自定义的内容不同的话,宽和高还真能调节,调整不正确的话,可能有的内容就不能显示。还有就是换行的行距也不能设置,一切都是默认值,如果是B/S模式就太简单了。
网上找了下个例子(百度就可以找到).以下是那位仁兄的:
namespace compass.Common
{
public partial class myLabel : System.Windows.Forms.Label
{
int lineDistance = 5;//行间距
public int LineDistance
{
get { return lineDistance; }
set { lineDistance = value; }
}
public myLabel()
{
InitializeComponent(); //这行有问题
}
public myLabel(IContainer container)
{
container.Add(this);
InitializeComponent(); //这行有问题
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
String drawString = this.Text;
Font drawFont = this.Font;
SolidBrush drawBrush = new SolidBrush(this.ForeColor);
SizeF textSize = g.MeasureString(this.Text, this.Font);//文本的矩形区域大小
int lineCount = Convert.ToInt16(textSize.Width / this.Width) + 1;//计算行数
this.Height = Convert.ToInt16((textSize.Height + lineDistance) * lineCount);//计算调整后的高度
this.AutoSize = false;
float x = 0.0F;
float y = 0.0F;
StringFormat drawFormat = new StringFormat();
int step = 1;
lineCount = drawString.Length;//行数不超过总字符数目
for (int i = 0; i < lineCount; i++)
{
//计算每行容纳的字符数目
int charCount;
for (charCount = 0; charCount < drawString.Length; charCount++)
{
string subN = drawString.Substring(0, charCount);
string subN1 = drawString.Substring(0, charCount + 1);
if (g.MeasureString(subN, this.Font).Width <= this.Width
&& g.MeasureString(subN1, this.Font).Width > this.Width)
{
step = charCount;
break;
}
}
string subStr;
if (charCount == drawString.Length)//最后一行文本
{
subStr = drawString;
e.Graphics.DrawString(subStr, drawFont, drawBrush, x, Convert.ToInt16(textSize.Height * i) + i * LineDistance, drawFormat);
break;
}
else
{
subStr = drawString.Substring(0, step);//当前行文本
drawString = drawString.Substring(step);//剩余文本
e.Graphics.DrawString(subStr, drawFont, drawBrush, x, Convert.ToInt16(textSize.Height * i) + i * LineDistance, drawFormat);
}
}
}
}
}
发现上面的控件存在两个问题:
1、控件的最终高度是窗体Paint的时候计算出来。控件运行时前期的高度会保持控件设计时的高度。只Paint以后高度才会重新计算。
2、效率。在计算行字数时这里应用的循环算法效率不高。而且外循环是drawstring.Length,即文本的数量。虽然这里会提前结束循环。但内循环仍然效率不高。
以下是我针对上述问题做的改进。如下代码:
namespace Tom.Component
{
public partial class LabelTx : System.Windows.Forms.Label
{
int lineDistance = 5;//行间距
Graphics gcs;
int iHeight = 0, height = 200;
string[] nrLine;
string[] nrLinePos;
int searchPos = 0;
int section = 1;
public int LineDistance
{
get { return lineDistance; }
set
{
lineDistance = value;
Changed(this.Font, this.Width, this.Text);
}
}
public LabelTx()
: base()
{
//this.TextChanged += new EventHandler(LabelTx_TextChanged);
this.SizeChanged += new EventHandler(LabelTx_SizeChanged);
this.FontChanged += new EventHandler(LabelTx_FontChanged);
//this.Font = new Font(this.Font.FontFamily, this.Font.Size, GraphicsUnit.Pixel);
}
void LabelTx_FontChanged(object sender, EventArgs e)
{
Changed(this.Font, this.Width, this.Text);
}
void LabelTx_SizeChanged(object sender, EventArgs e)
{
Changed(this.Font, this.Width, this.Text);
}
public LabelTx(IContainer container)
{
container.Add(this);
//base.Height
//InitializeComponent();
}
public int FHeight
{
get { return this.Font.Height; }
}
protected int Height
{
get { return height; }
set
{
height = value;
base.Height = value;
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
//is.Font.Size.
base.Text = value;
Changed(this.Font, this.Width, value);
}
}
protected void Changed(Font ft, int iWidth, string value)
{
iHeight = 0;
if (value != "")
{
if (gcs == null)
{
gcs = this.CreateGraphics();
SizeF sf0 = gcs.MeasureString(new string('测', 20), ft);
searchPos = (int)(iWidth * 20 / sf0.Width);
}
nrLine = value.Split(new string[1] { "/r/n" }, StringSplitOptions.RemoveEmptyEntries);
section = nrLine.Length;
nrLinePos = new string[section];
SizeF sf1, sf2;
string temps, tempt;
string drawstring;
int temPos, ipos;
for (int i = 0; i < section; i++)
{
ipos = 0;
temPos = searchPos;
if (searchPos >= nrLine[i].Length)
{
ipos+=nrLine[i].Length;
nrLinePos[i] += "," + ipos.ToString();
iHeight++;
continue;
}
drawstring = nrLine[i];
nrLinePos[i] = "";
while (drawstring.Length > searchPos)
{
bool isfind = false;
for (int j = searchPos; j < drawstring.Length; j++)
{
temps = drawstring.Substring(0, j);
tempt = drawstring.Substring(0, j + 1);
sf1 = gcs.MeasureString(temps, ft);
sf2 = gcs.MeasureString(tempt, ft);
if (sf1.Width < iWidth && sf2.Width > iWidth)
{
iHeight++;
ipos += j;
nrLinePos[i] += "," + ipos.ToString();
isfind = true;
drawstring = drawstring.Substring(j);
break;
}
}
if (!isfind)
{
//drawstring = drawstring.Substring(searchPos);
//iHeight++;
break;
}
}
ipos += drawstring.Length;
nrLinePos[i] += "," + ipos.ToString();
iHeight++;
//tempLine = (int)(sf1.Width - 1) / this.Width + 1;
//iHeight += tempLine;
}
}
this.Height = iHeight * (ft.Height + lineDistance);
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
//base.OnPaint(e);
//if (isPaint) return;
//isPaint = true;
Graphics g = e.Graphics;
String drawString = this.Text;
Font drawFont = this.Font;
SolidBrush drawBrush = new SolidBrush(this.ForeColor);
SizeF textSize = g.MeasureString(this.Text, this.Font);//文本的矩形区域大小
int lineCount = Convert.ToInt16(textSize.Width / this.Width) + 1;//计算行数
int fHeight = this.Font.Height;
int htHeight = 0;
this.AutoSize = false;
float x = 0.0F;
float y = 0.0F;
StringFormat drawFormat = new StringFormat();
int step = 1;
bool isFirst = true;
SizeF sf1, sf2;
string subN, subN1;
lineCount = drawString.Length;//行数不超过总字符数目
int i, idx, first;
string subStr, tmpStr="", midStr = "";
string[] idxs;
for (i = 0; i < section; i++)
{
first = 0;
subStr = nrLine[i];
if (nrLinePos[i]!=null) tmpStr = nrLinePos[i].TrimStart(',');
midStr = subStr.Substring(first);
if (tmpStr != "")
{
idxs = tmpStr.Split(',');
for (int j = 0; j < idxs.Length; j++)
{
idx = int.Parse(idxs[j]);
midStr = subStr.Substring(first, idx - first);
e.Graphics.DrawString(midStr, drawFont, drawBrush, x, Convert.ToInt16(htHeight), drawFormat);
htHeight += (fHeight + lineDistance);
first = idx;
}
//midStr = subStr.Substring(first);
}
//e.Graphics.DrawString(midStr, drawFont, drawBrush, x, Convert.ToInt16(htHeight), drawFormat);
//htHeight += ( lineDistance);//fHeight +
}
}
}
}
当然这里改动下,设置段落高度。有兴趣可以改下。