VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • c#中组件的设计技术兼谈TProgressBar实现

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

  一直用Delphi做应用系统,比较而言VS 2005提供的组件不够灵活美观。例如,进度条ProgressBar,样式过于单一。于是,利用假期时间,学习学习C#的组件设计技术,搞个灵活点的进度条组件。设计要求:既可以选择进度条式样,也可以显示进度数字。

  直接从ProgressBar派生类是行不通的,因为ProgressBar根本不允许override核心方法OnPaint()。用“how to”、“progress”、“smooth”关键字Google一下,找到最权威的代码文章:http://support.microsoft.com/kb/323116。其基本思路如下:

  1)派生自UserControl,重写OnPaint()方法

  2)在Value变化时,捕获到变化区域UpdateRect,只对该区域标记为无效,即:Invalidate(UpdateRect),这样做的目的是,消除了那种快速变化时背景(白色)闪动的感觉。如果直接刷新当前Value值对应左边全部区域,将有空白闪动的现象。

  按上述思路编写的进度条的确比自己做的效果好多了,但存在如下问题:

  1)原文中忘记了Update方法,即在Update(UpdateRect)后,应立即调用控件的Update()方法;

  2)实现了Smooth进度条,但也丢失了ProgressBar的Block的美观;

  3)如果直接在OnPaint()方法中显示进度条百分比,必须Invalidate()文字区域。但使用时还是有一小块背景(白色)闪动的感觉,不够圆满。

  多次尝试后,发现直接从Label组件派生,既可以显示数字、还可直接Invalidate()全部左边区域且无闪动感觉,比上文的实现简单。主要代码如下:

protected override void OnPaint(PaintEventArgs e)
{
if (m_ProgressBarBorderStyle == TBorderStyle.Fixed3D)
{
this.Draw3DBorder(e.Graphics);
}
else if (m_ProgressBarBorderStyle == TBorderStyle.Single)
{
this.DrawSingleBoard(e.Graphics);
}
else if (m_ProgressBarBorderStyle == TBorderStyle.Fixed2D)
{
this.Draw2DBorder(e.Graphics);
}
  
this.DrawProgressBar(e.Graphics);
  
if (m_ProgressBarPercent)
{
base.Text = ((double)m_Value / (double)m_Maximum).ToString("##0 %");
}
else
{
base.Text = string.Empty;
}
  
base.OnPaint(e);
}
private void DrawProgressBar(Graphics g)
{
int top = this.ClientRectangle.Top + this.GetTopOffSet();
int height = this.ClientRectangle.Height - this.GetTopOffSet() * 2;
double percent = (double)m_Value / (double)m_Maximum;
if (percent > 1.0)
{
percent = 1.0;
}
int valueWidth = (int)((this.ClientRectangle.Width - this.GetLeftOffSet() * 2) * percent); // 值对应的宽度
int blockWidth = (valueWidth / (m_ProgressBarBlockWidth + m_ProgressBarBlockSpace)) * (m_ProgressBarBlockWidth + m_ProgressBarBlockSpace); // 对应实际绘制块的宽度
if (percent > 0.99) // 防止舍入误差, 补充块长度
{
if (this.ClientRectangle.Width - this.GetLeftOffSet() * 2 - blockWidth > 0)
{
blockWidth += (this.ClientRectangle.Width - this.GetLeftOffSet() * 2 - blockWidth) / (m_ProgressBarBlockWidth + m_ProgressBarBlockSpace);
}
}
int left = this.ClientRectangle.Left + this.GetLeftOffSet();
int filledWidth = m_ProgressBarBlockWidth + m_ProgressBarBlockSpace;
while (filledWidth <= blockWidth)
{
g.FillRectangle(m_ProgressBarFillBrush, left, top, m_ProgressBarBlockWidth, height);
left += m_ProgressBarBlockWidth + m_ProgressBarBlockSpace;
filledWidth += m_ProgressBarBlockWidth + m_ProgressBarBlockSpace;
}
int lastBarWidth = this.ClientRectangle.Width - left - this.GetLeftOffSet();
if (lastBarWidth > 0 && lastBarWidth < m_ProgressBarBlockWidth + m_ProgressBarBlockSpace) // 剩下的尾巴不能绘制了
{
filledWidth = this.ClientRectangle.Width - left - this.GetLeftOffSet();
if (filledWidth > 0)
{
g.FillRectangle(m_ProgressBarFillBrush, left, top, filledWidth, height); // 绘制部分 bar
}
}
}

实现时碰到的另一个技术难题就是,有两个Label属性必须在TProgressBar构造函数中设置其初始值:base.AutoSize = false、base.TextAlign = ContentAlignment.MiddleCenter。实际情况是,在构造函数中赋值根本不起作用。因为这两个属性值将由窗体的InitializeComponent()方法给值。显然,这两个属性在Label中是特殊标记的,即有如下类似语句:

 

[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool AutoSize
{get;set}

  经修改后,部分代码如下:

public class TProgressBar: Label
{
public TProgressBar()
{
base.AutoSize = false; // AutoSize 是 Designer 属性
base.TextAlign = ContentAlignment.MiddleCenter; // TextAlign 是 Designer 属性
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] // 不在窗体中产生该属性语句
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] // 在属性编辑器中不显示
public new bool AutoSize
{
get
{
return base.AutoSize; // 在构造函数中设置值、屏蔽该属性
}
}
}

 

 

[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool AutoSize
{get;set}

  经修改后,部分代码如下:

public class TProgressBar: Label
{
public TProgressBar()
{
base.AutoSize = false; // AutoSize 是 Designer 属性
base.TextAlign = ContentAlignment.MiddleCenter; // TextAlign 是 Designer 属性
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] // 不在窗体中产生该属性语句
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] // 在属性编辑器中不显示
public new bool AutoSize
{
get
{
return base.AutoSize; // 在构造函数中设置值、屏蔽该属性
}
}
}

 



相关教程