Window Form步骤条控件实现

简介: 本文将利用C#中的GDI+技术,自动绘制相关的UI元素,实现Window Form的步骤条控件。

     在当前的不少电商或者物流等应用程序中,为了清晰的表明某些事件的当前状态,以及历史时序记录情况,经常可以看到一个步骤条控件,它分成几个节点,每个节点代表一个核心状态,每个状态之间通过线条进行连接,以经过的节点高亮显示,未经过的线条灰度显示。其中,每个节点下可以通过文字进行简要的描述。本文将利用C#中的GDI+技术,自动绘制相关的UI元素,实现Window Form的步骤条控件。

1 项目结构


    利用Visual Studio 社区版,创建一个Window应用程序项目WinControls,其中在资源文件中添加一个图形,用于绘制经过步骤条节点的✔ 状态。并添加几个类,具体项目结构如下图所示:

1.png

其中的check_lightblue.png图片代表的是✔ 状态。可以通过Properties.Resources.check_lightblue进行访问。eumStepState.cs是一个枚举类型,表示节点的状态信息,核心代码如下:

namespaceWinControls{
publicenumeumStepState    {
Waiting,
Completed,
OutTime    }
}

而 StepEntity.cs 文件是代表一个步骤条节点的实体对象,其中具备的属性有节点ID,节点名称,节点状态,节点顺序,节点描述等,核心代码如下:

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading.Tasks;
namespaceWinControls{
publicclassStepEntity    {
publicstringId { get; set; }
publicstringStepName { get; set; }
publicintStepOrder { get; set; }
publiceumStepStateStepState { get; set; }
publicstringStepDesc { get; set; }
publicobjectStepTag { get; set; }
publicStepEntity(stringid, stringstepname, intsteporder, 
stringstepdesc, eumStepStatestepstate, objecttag)
        {
this.Id=id;
this.StepName=stepname;
this.StepOrder=steporder;
this.StepDesc=stepdesc;
this.StepTag=tag;
this.StepState=stepstate;
        }
    }
}

2 步骤条实现


    在项目WinControls中添加一个名为StepViewer的用户控件,具体如下图所示:

2.png

核心代码如下:

usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading.Tasks;
usingSystem.Windows.Forms;
namespaceWinControls{
publicpartialclassStepViewer : UserControl    {
publicStepViewer()
        {
InitializeComponent();
this.Height=68;
this.Paint+=StepViewer_Paint;
        }
privateList<StepEntity>_dataSourceList=null;
privateColor_Gray=Color.FromArgb(189, 195, 199);
privateColor_DarkGray=Color.FromArgb(149, 165, 166);
privateColor_Blue=Color.FromArgb(52, 152, 219);
privateColor_Red=Color.FromArgb(231, 76, 60);
        [Browsable(true), Category("StepViewer")]
publicList<StepEntity>ListDataSource        {
get            {
return_dataSourceList;
            }
set            {
if (_dataSourceList!=value)
                {
_dataSourceList=value;
Invalidate();
                }
            }
        }
privateint_currentStep=0;
publicintCurrentStep {
get            {
return_currentStep;
            }
set            {
if (_currentStep!=value)
                {
_currentStep=value;
Invalidate();
                }
            }
        }
privatevoidStepViewer_Paint(objectsender, PaintEventArgse)
        {
if (this.ListDataSource!=null)
            {
intCenterY=this.Height/2;
intindex=1;
intcount=ListDataSource.Count;
intlineWidth=120;
intStepNodeWH=28;
//this.Width = 32 * count + lineWidth * (count - 1) + 6+300;//defalut pen & brushe.Graphics.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.HighQuality;
Brushbrush=newSolidBrush(_Gray);
Penp=newPen(brush, 1f);
BrushbrushNode=newSolidBrush(_DarkGray);
PenpenNode=newPen(brushNode, 1f);
BrushbrushNodeCompleted=newSolidBrush(_Blue);
PenpenNodeCompleted=newPen(brushNodeCompleted, 1f);
intinitX=6;
//stringFontnFont=newFont("微软雅黑", 12);
FontstepFont=newFont("微软雅黑", 11, FontStyle.Bold);
intNodeNameWidth=0;
foreach (variteminListDataSource)
                {
//roundRectanglerec=newRectangle(initX, CenterY-StepNodeWH/2, StepNodeWH, StepNodeWH);
if (CurrentStep==item.StepOrder)
                    {
if (item.StepState==eumStepState.OutTime)
                        {
e.Graphics.DrawEllipse(newPen(_Red, 1f), rec);
e.Graphics.FillEllipse(newSolidBrush(_Red), rec);
                        }
else                        {
e.Graphics.DrawEllipse(penNodeCompleted, rec);
e.Graphics.FillEllipse(brushNodeCompleted, rec);
                        }
//白色字体SizeFfTitle=e.Graphics.MeasureString(index.ToString(), stepFont);
PointpTitle=newPoint(initX+StepNodeWH/2- (int)Math.Round(fTitle.Width) /2, CenterY- (int)Math.Round(fTitle.Height/2));
e.Graphics.DrawString(index.ToString(), stepFont, Brushes.White, pTitle);
//nodeNameSizeFsNode=e.Graphics.MeasureString(item.StepName, nFont);
PointpNode=newPoint(initX+StepNodeWH, CenterY- (int)Math.Round(sNode.Height/2) +2);
e.Graphics.DrawString(item.StepName, newFont(nFont, FontStyle.Bold), brushNode, pNode);
NodeNameWidth= (int)Math.Round(sNode.Width);
if (index<count)
                        {
e.Graphics.DrawLine(p, initX+StepNodeWH+NodeNameWidth, CenterY, initX+StepNodeWH+NodeNameWidth+lineWidth, CenterY);
                        }
                    }
elseif (item.StepOrder<CurrentStep)
                    {
//completede.Graphics.DrawEllipse(penNodeCompleted, rec);
//imageRectangleFrecRF=newRectangleF(rec.X+6, rec.Y+6, rec.Width-12, rec.Height-12);
e.Graphics.DrawImage(Properties.Resources.check_lightblue, recRF);
//nodeNameSizeFsNode=e.Graphics.MeasureString(item.StepName, nFont);
PointpNode=newPoint(initX+StepNodeWH, CenterY- (int)Math.Round(sNode.Height/2) +2);
e.Graphics.DrawString(item.StepName, nFont, brushNode, pNode);
NodeNameWidth= (int)Math.Round(sNode.Width);
if (index<count)
                        {
e.Graphics.DrawLine(penNodeCompleted, initX+StepNodeWH+NodeNameWidth, CenterY, initX+StepNodeWH+NodeNameWidth+lineWidth, CenterY);
                        }
                    }
else                    {
e.Graphics.DrawEllipse(p, rec);
//SizeFfTitle=e.Graphics.MeasureString(index.ToString(), stepFont);
PointpTitle=newPoint(initX+StepNodeWH/2- (int)Math.Round(fTitle.Width) /2, CenterY- (int)Math.Round(fTitle.Height/2));
e.Graphics.DrawString(index.ToString(), stepFont, brush, pTitle);
//nodeNameSizeFsNode=e.Graphics.MeasureString(item.StepName, nFont);
PointpNode=newPoint(initX+StepNodeWH, CenterY- (int)Math.Round(sNode.Height/2) +2);
e.Graphics.DrawString(item.StepName, nFont, brushNode, pNode);
NodeNameWidth= (int)Math.Round(sNode.Width);
if (index<count)
                        {
//linee.Graphics.DrawLine(p, initX+StepNodeWH+NodeNameWidth, CenterY, initX+StepNodeWH+NodeNameWidth+lineWidth, CenterY);
                        }
                    }
//描述信息if (item.StepDesc!="")
                    {
PointpNode=newPoint(initX+StepNodeWH, CenterY+10);
e.Graphics.DrawString(item.StepDesc, newFont(nFont.FontFamily, 10), brush, pNode);
                    }
index++;
//8 is space widthinitX=initX+lineWidth+StepNodeWH+NodeNameWidth+8;
                }
            }
        }
    }
}

其中,首先定义了一组颜色,代码如下:

privateColor_Gray=Color.FromArgb(189, 195, 199); 
privateColor_DarkGray=Color.FromArgb(149, 165, 166);
privateColor_Blue=Color.FromArgb(52, 152, 219);
privateColor_Red=Color.FromArgb(231, 76, 60);

其次,由于步骤条的节点有多个,是一个列表,因此这里用private List<StepEntity> _dataSourceList = null;进行定义一个数据源。[Browsable(true), Category("StepViewer")]则表示ListDataSource属性在控件的属性列表中可见。在赋值后会调用 Invalidate()方法进行UI重绘。

再次,此控件初始化时,执行如下代码:

publicStepViewer()
 {
InitializeComponent();
this.Height=68;
this.Paint+=StepViewer_Paint;
 }

即限定控件的高度为68,同时绑定绘制事件Paint,实现绘制的方法为 StepViewer_Paint,这是控件的核心,其中使用了  e.Graphics下的API可以绘制圆形,线条和图片,以及文本信息。

3 步骤条效果


    将控件添加到Form1窗口上并在初始化方法中维护数据源信息,核心代码如下:

usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading.Tasks;
usingSystem.Windows.Forms;
namespaceWinControls{
publicpartialclassForm1 : Form    {
publicForm1()
        {
InitializeComponent();
this.BackColor=Color.White;
        }
privatevoidForm1_Load(objectsender, EventArgse)
        {
List<StepEntity>list=newList<StepEntity>();
list.Add(newStepEntity("1", "新开单", 1, "这里是该步骤的描述信息", eumStepState.Completed, null));
list.Add(newStepEntity("2", "主管审批", 2, "这里是该步骤的描述信息", eumStepState.Waiting, null));
list.Add(newStepEntity("3", "总经理审批", 3, "这里是该步骤的描述信息", eumStepState.OutTime, null));
list.Add(newStepEntity("2", "完成", 4, "这里是该步骤的描述信息", eumStepState.Waiting, null));
this.stepViewer1.CurrentStep=3;
this.stepViewer1.ListDataSource=list;
        }
privatevoidbutton1_Click(objectsender, EventArgse)
        {
this.stepViewer1.CurrentStep--;
        }
privatevoidbutton2_Click(objectsender, EventArgse)
        {
this.stepViewer1.CurrentStep++;
        }
    }
}

执行项目,Form1界面具体如下图所示:

3.png

相关文章
|
JavaScript 前端开发
12EasyUI 菜单与按钮- 创建分割按钮(Split Button)
12EasyUI 菜单与按钮- 创建分割按钮(Split Button)
87 0
|
JavaScript
element-ui的popover弹出框点击取消
element-ui的popover弹出框点击取消
268 1
element-ui输入框中添加按钮
element-ui输入框中添加按钮
771 3
element-ui输入框中添加按钮
|
JavaScript
element-ui 的表格里面使用 el-popover 实现编辑弹层功能
element-ui 的表格里面使用 el-popover 实现编辑弹层功能
908 0
element-ui 的表格里面使用 el-popover 实现编辑弹层功能
|
前端开发
【解决方法】element-ui 中输入框input与下拉选择框没有在一条水平线上
【解决方法】element-ui 中输入框input与下拉选择框没有在一条水平线上
908 0
【解决方法】element-ui 中输入框input与下拉选择框没有在一条水平线上
element ui confirm弹窗自定义样式小知识
element ui confirm弹窗自定义样式小知识
1693 0
Element UI - 打开弹出框(el-dialog)页面会抖动是什么原因?
Element UI - 打开弹出框(el-dialog)页面会抖动是什么原因?
1564 0
|
存储 数据可视化 程序员
Window Form 控件基础
在开始正式介绍如何开发自定义控件之前,有必要先了解一下控件开发的基础知识。下面从控件的概念、分类和开发模式上对控件做一个基本的概述。
866 0
Window Form 控件基础