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

相关文章
|
API C# 开发者
一款开源免费美观的WinForm UI控件库
一款开源免费美观的WinForm UI控件库
799 9
|
Linux 开发工具 Windows
设备接入--海康摄像头SDK
springboot-对接海康摄像头,兼容window和Linux环境
4782 3
设备接入--海康摄像头SDK
|
2月前
|
人工智能 运维 BI
Top5 主流工单管理系统全对比(2025 版):功能、价格、行业适配性详解
在数字化浪潮推动下,工单管理系统已成为企业提升运营效率、优化客户体验的关键工具。本文解析其核心价值与选型要点,并对合力亿捷、Zendesk、Freshdesk、Jira Service Management、钉钉宜搭五大主流系统进行多维度对比,涵盖功能、价格、行业适配性等,助力企业精准选型,加速数字化转型进程。
|
11月前
|
人工智能 API Apache
推荐3款开源、美观且免费的WinForm UI控件库
推荐3款开源、美观且免费的WinForm UI控件库
1944 6
|
设计模式 开发框架 前端开发
基于DevExpress的GridControl实现的一些界面处理功能
基于DevExpress的GridControl实现的一些界面处理功能
解决方案-CMake error: error in configuration process, project files may be invalid(Windows&VS可参考)
解决方案-CMake error: error in configuration process, project files may be invalid(Windows&VS可参考)
4378 0
|
分布式计算 JavaScript 前端开发
JavaScript:轻松创建二维数组的多种方法
本文探讨了如何使用 JavaScript 生成二维数组的多种方法,揭示了这些方法在编程中的灵活应用。文章首先通过介绍如何创建一维数组,为理解二维数组的生成奠定基础。接着,分析了几种常见的生成二维数组的方法,包括使用嵌套循环、Array.from()、Array.fill() 以及 展开运算符 和 map() 等技巧。每种方法都有其优缺点,开发者可以根据场景选择最合适的方案。
402 0
Vue2——监听页面滚动实现菜单和页面对应
Vue2——监听页面滚动实现菜单和页面对应
151 1
|
Docker 容器
.NET Core 网站项目在Docker中运行无法访问问题处理
.NET Core 网站项目在Docker中运行无法访问问题处理
707 0
|
SQL 存储 消息中间件
流批一体的近实时数仓的思考与设计
欢迎大家多分享具体实践,一起共筑新的数据实践方式。
10231 3
流批一体的近实时数仓的思考与设计