在C# WinForms项目开发中,步骤条控件常用于多步骤表单、安装向导、流程审批等场景,能直观展示当前所处步骤和整体进度。原生WinForms没有内置的Step步骤条控件,我们可以通过自定义控件的方式实现符合需求的步骤条组件。

步骤条控件需求分析
我们要实现的Step步骤条控件需要具备以下核心功能:
- 支持动态设置步骤数量、每个步骤的显示文本
- 区分未开始、进行中、已完成三种步骤状态,用不同颜色样式展示
- 支持点击步骤切换当前选中状态,触发对应事件
- 支持设置步骤条的整体尺寸、字体、颜色等样式属性
控件基础结构定义
首先创建自定义控件类,继承自UserControl,定义步骤相关的属性和状态枚举:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace CustomControls
{
// 步骤状态枚举
public enum StepStatus
{
Unfinished, // 未开始
Processing, // 进行中
Finished // 已完成
}
// 单个步骤项类
public class StepItem
{
public string Text { get; set; } // 步骤显示文本
public StepStatus Status { get; set; } // 步骤状态
public Rectangle Bounds { get; set; } // 步骤绘制区域
}
public partial class StepBar : UserControl
{
// 步骤集合
private List<StepItem> _steps = new List<StepItem>();
// 当前选中步骤索引
private int _currentStepIndex = 0;
// 步骤圆点直径
private int _stepDotSize = 24;
// 步骤文本字体
private Font _stepFont = new Font("微软雅黑", 9f);
// 未开始状态颜色
public Color UnfinishedColor { get; set; } = Color.LightGray;
// 进行中状态颜色
public Color ProcessingColor { get; set; } = Color.DodgerBlue;
// 已完成状态颜色
public Color FinishedColor { get; set; } = Color.LimeGreen;
public StepBar()
{
InitializeComponent();
// 开启双缓冲避免闪烁
this.DoubleBuffered = true;
this.Resize += StepBar_Resize;
}
// 添加步骤
public void AddStep(string text)
{
_steps.Add(new StepItem { Text = text, Status = StepStatus.Unfinished });
// 第一个步骤默认设为进行中
if (_steps.Count == 1)
{
_steps[0].Status = StepStatus.Processing;
}
UpdateStepBounds();
this.Invalidate();
}
// 切换到指定步骤
public void GoToStep(int index)
{
if (index < 0 || index >= _steps.Count) return;
// 更新之前步骤状态
for (int i = 0; i < _steps.Count; i++)
{
if (i < index)
{
_steps[i].Status = StepStatus.Finished;
}
else if (i == index)
{
_steps[i].Status = StepStatus.Processing;
}
else
{
_steps[i].Status = StepStatus.Unfinished;
}
}
_currentStepIndex = index;
this.Invalidate();
}
}
}步骤布局与绘制逻辑
接下来实现步骤的布局计算和绘制方法,计算每个步骤的位置和连接线区域:
private void UpdateStepBounds()
{
if (_steps.Count == 0) return;
int totalWidth = this.Width - 20;
int stepWidth = totalWidth / _steps.Count;
int yCenter = this.Height / 2;
for (int i = 0; i < _steps.Count; i++)
{
int x = 10 + stepWidth * i + stepWidth / 2 - _stepDotSize / 2;
int y = yCenter - _stepDotSize / 2;
_steps[i].Bounds = new Rectangle(x, y, _stepDotSize, _stepDotSize);
}
}
private void StepBar_Resize(object sender, EventArgs e)
{
UpdateStepBounds();
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (_steps.Count == 0) return;
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
int yCenter = this.Height / 2;
// 绘制步骤连接线
for (int i = 0; i < _steps.Count - 1; i++)
{
Rectangle currentBounds = _steps[i].Bounds;
Rectangle nextBounds = _steps[i + 1].Bounds;
Point start = new Point(currentBounds.Right, yCenter);
Point end = new Point(nextBounds.Left, yCenter);
// 两个步骤都完成则连线为完成色,否则为未完成色
Color lineColor = (_steps[i].Status == StepStatus.Finished && _steps[i+1].Status != StepStatus.Unfinished)
? FinishedColor
: UnfinishedColor;
using (Pen linePen = new Pen(lineColor, 2))
{
g.DrawLine(linePen, start, end);
}
}
// 绘制每个步骤的圆点和文本
for (int i = 0; i < _steps.Count; i++)
{
StepItem step = _steps[i];
Color dotColor = UnfinishedColor;
Color textColor = Color.Gray;
string drawText = (i + 1).ToString();
// 根据状态设置颜色
switch (step.Status)
{
case StepStatus.Processing:
dotColor = ProcessingColor;
textColor = ProcessingColor;
break;
case StepStatus.Finished:
dotColor = FinishedColor;
textColor = FinishedColor;
break;
}
// 绘制圆点
using (SolidBrush dotBrush = new SolidBrush(dotColor))
{
g.FillEllipse(dotBrush, step.Bounds);
}
// 绘制圆点内的数字
using (SolidBrush textBrush = new SolidBrush(Color.White))
{
SizeF textSize = g.MeasureString(drawText, _stepFont);
float textX = step.Bounds.X + (step.Bounds.Width - textSize.Width) / 2;
float textY = step.Bounds.Y + (step.Bounds.Height - textSize.Height) / 2;
g.DrawString(drawText, _stepFont, textBrush, textX, textY);
}
// 绘制步骤下方文本
using (SolidBrush textBrush = new SolidBrush(textColor))
{
SizeF textSize = g.MeasureString(step.Text, _stepFont);
float textX = step.Bounds.X + (step.Bounds.Width - textSize.Width) / 2;
float textY = step.Bounds.Bottom + 5;
g.DrawString(step.Text, _stepFont, textBrush, textX, textY);
}
}
}交互事件实现
添加鼠标点击事件,支持点击步骤切换状态,同时定义步骤切换事件供外部订阅:
// 步骤切换事件
public event Action<int, StepItem> StepChanged;
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
// 判断点击位置是否在某个步骤区域内
for (int i = 0; i < _steps.Count; i++)
{
if (_steps[i].Bounds.Contains(e.Location))
{
GoToStep(i);
StepChanged?.Invoke(i, _steps[i]);
break;
}
}
}控件使用示例
在WinForms窗体中使用我们开发的Step步骤条控件:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// 初始化步骤条
stepBar1.AddStep("填写基本信息");
stepBar1.AddStep("上传证明材料");
stepBar1.AddStep("确认提交");
stepBar1.AddStep("完成");
// 订阅步骤切换事件
stepBar1.StepChanged += (index, step) =>
{
MessageBox.Show($"切换到步骤{index + 1}:{step.Text}");
};
// 模拟下一步按钮点击
btnNext.Click += (s, e) =>
{
if (stepBar1._currentStepIndex < stepBar1._steps.Count - 1)
{
stepBar1.GoToStep(stepBar1._currentStepIndex + 1);
}
};
}
}总结
通过以上步骤,我们就完成了一个基础可用的C# Step步骤条控件开发,支持状态切换、样式自定义和交互事件。实际项目中可以根据需求扩展更多功能,比如支持自定义步骤图标、横向/纵向布局切换、步骤禁用状态等,核心思路都是围绕步骤状态管理和绘制逻辑展开。
C#Step步骤条WinForms控件自定义控件流程导航修改时间:2026-05-29 04:36:55