CustomScrollListView
是一个基于 C# 和 GDI+ 的自定义控件,继承自 Control
类,能够实现具有列头和项目的滚动列表视图。此控件支持多列展示,并可以通过定时器实现自动滚动效果,使得用户可以流畅地查看列表内容。
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
namespace AppControls
{
public class CustomScrollListView : Control
{
private List<ColumnHeader> _columns;
private List<List<string>> _columnItems;
private int[] _currentIndex;
private Timer _scrollTimer;
private int _scrollSpeed = 2; // 像素/每次滚动
private int _itemHeight = 30; // 每个条目的高度
private int _headerHeight = 40; // 列头高度
private float _currentOffset = 0;
private bool _isScrolling = false;
private int _visibleItemCount = 4; // 可见条目数量
private int _columnCount = 1; // 列数
public CustomScrollListView()
{
_columns = new List<ColumnHeader>();
_columnItems = new List<List<string>>();
_columnCount = 1;
_currentIndex = new int[_columnCount];
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
UpdateStyles();
_scrollTimer = new Timer();
_scrollTimer.Interval = 20; // 每20毫秒滚动一次
_scrollTimer.Tick += ScrollTimer_Tick;
}
/// <summary>
/// 获取或设置可见的条目数量
/// </summary>
[Category("Custom Properties")]
[Description("设置列表中可见的条目数量")]
[DefaultValue(4)]
public int VisibleItemCount
{
get { return _visibleItemCount; }
set
{
if (value > 0)
{
_visibleItemCount = value;
// 重新调整控件高度
Height = _headerHeight + (_visibleItemCount * _itemHeight);
// 重新绘制
Invalidate();
}
else
{
throw new ArgumentException("可见条目数量必须大于0");
}
}
}
// 列头类
public class ColumnHeader
{
public string Text { get; set; }
public Color BackColor { get; set; } = Color.LightGray;
public Color ForeColor { get; set; } = Color.Black;
public int Width { get; set; }
}
// 添加列头方法
public void AddColumn(string text, int width = 100, Color? backColor = null, Color? foreColor = null)
{
var column = new ColumnHeader
{
Text = text,
Width = width,
BackColor = backColor ?? Color.LightGray,
ForeColor = foreColor ?? Color.Black
};
_columns.Add(column);
_columnItems.Add(new List<string>());
_columnCount = _columns.Count;
_currentIndex = new int[_columnCount];
Invalidate();
}
// 修改列头属性
public void SetColumnHeader(int columnIndex, string text = null, int? width = null, Color? backColor = null, Color? foreColor = null)
{
if (columnIndex >= 0 && columnIndex < _columns.Count)
{
var column = _columns[columnIndex];
if (text != null)
column.Text = text;
if (width.HasValue)
column.Width = width.Value;
if (backColor.HasValue)
column.BackColor = backColor.Value;
if (foreColor.HasValue)
column.ForeColor = foreColor.Value;
Invalidate();
}
}
[Category("Custom Properties")]
[Description("列头高度")]
public int HeaderHeight
{
get { return _headerHeight; }
set
{
_headerHeight = value;
Invalidate();
}
}
// 为指定列添加条目
public void AddItemToColumn(int columnIndex, string item)
{
if (columnIndex >= 0 && columnIndex < _columnCount)
{
_columnItems[columnIndex].Add(item);
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (_columnItems.Count == 0) return;
Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
g.Clear(BackColor);
// 计算每列宽度
int availableWidth = Width;
int[] columnWidths = new int[_columnCount];
// 优先使用自定义宽度,否则平均分配
for (int col = 0; col < _columnCount; col++)
{
columnWidths[col] = _columns[col].Width > 0
? _columns[col].Width
: availableWidth / _columnCount;
}
int totalHeaderWidth = this.Width;
g.FillRectangle(new SolidBrush(_columns[0].BackColor), 0, 0, totalHeaderWidth, _headerHeight);
// 第一步:绘制固定的列头 - 不受滚动影响
g.SetClip(new Rectangle(0, 0, Width, _headerHeight));
for (int col = 0; col < _columnCount; col++)
{
int xPos = columnWidths.Take(col).Sum();
// 绘制列头背景
using (SolidBrush headerBrush = new SolidBrush(_columns[col].BackColor))
{
g.FillRectangle(headerBrush, xPos, 0, columnWidths[col], _headerHeight);
}
// 绘制列头文本
using (SolidBrush textBrush = new SolidBrush(_columns[col].ForeColor))
{
StringFormat sf = new StringFormat
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Center
};
g.DrawString(_columns[col].Text, Font, textBrush,
new RectangleF(xPos, 0, columnWidths[col], _headerHeight), sf);
}
}
g.ResetClip();
// 第二步:绘制滚动内容 - 只在列头下方区域绘制
g.SetClip(new Rectangle(0, _headerHeight + 1, Width, Height - _headerHeight));
// 定义分隔线颜色
Color separatorColor = Color.LightGray;
using (Pen separatorPen = new Pen(separatorColor, 1))
{
for (int col = 0; col < _columnCount; col++)
{
int xPos = columnWidths.Take(col).Sum();
int startIndex = _currentIndex[col];
for (int i = 0; i < _visibleItemCount; i++)
{
// 计算索引,确保即使是负数也能正确取值
int index = (startIndex + i + _columnItems[col].Count) % _columnItems[col].Count;
// 计算绘制位置 - 固定从列头高度后开始
float yPos = i * _itemHeight + _currentOffset + _headerHeight;
// 绘制条目背景
g.FillRectangle(Brushes.White, xPos, yPos, columnWidths[col], _itemHeight);
// 绘制底部分隔线
g.DrawLine(separatorPen, xPos, yPos + _itemHeight - 1, this.Width, yPos + _itemHeight - 1);
// 绘制条目文本
using (SolidBrush textBrush = new SolidBrush(ForeColor))
{
g.DrawString(_columnItems[col][index], Font, textBrush,
xPos, yPos + (_itemHeight - Font.Height) / 2);
}
}
}
}
g.ResetClip();
}
// 重写大小调整方法 - 固定高度
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
// 高度 = 列头高度 + 可见条目高度
Height = _headerHeight + (_visibleItemCount * _itemHeight);
Invalidate();
}
// 开始滚动
public void StartScroll()
{
bool canScroll = true;
for (int i = 0; i < _columnCount; i++)
{
if (_columnItems[i].Count <= _visibleItemCount)
{
canScroll = false;
break;
}
}
if (canScroll)
{
_isScrolling = true;
_currentOffset = 0;
_scrollTimer.Start();
}
}
// 停止滚动
public void StopScroll()
{
_scrollTimer.Stop();
_isScrolling = false;
_currentOffset = 0;
for (int i = 0; i < _columnCount; i++)
{
_currentIndex[i] = 0;
}
Invalidate();
}
// 滚动事件处理
private void ScrollTimer_Tick(object sender, EventArgs e)
{
// 累加偏移量
_currentOffset -= _scrollSpeed;
// 当偏移量超过一个条目高度时,切换到下一个条目
if (Math.Abs(_currentOffset) >= _itemHeight)
{
_currentOffset += _itemHeight; // 调整偏移量,保持连续性
// 更新每一列的索引
for (int i = 0; i < _columnCount; i++)
{
_currentIndex[i] = (_currentIndex[i] + 1) % _columnItems[i].Count;
}
}
Invalidate();
}
}
}
C#using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AppControls
{
public partial class Form17 : Form
{
private CustomScrollListView _customListView;
public Form17()
{
InitializeComponent();
// 创建自定义滚动列表
_customListView = new CustomScrollListView();
_customListView.BackColor = Color.White;
_customListView.ForeColor = Color.Black;
_customListView.Font = new Font("Arial", 10);
// 添加列头
_customListView.AddColumn("姓名", 100, Color.DarkGreen, Color.White);
_customListView.AddColumn("年龄", 80, Color.DarkGreen, Color.White);
_customListView.AddColumn("城市", 120, Color.DarkGreen, Color.White);
_customListView.HeaderHeight = 30; // 设置列头高度为50像素
// 为每一列添加数据
// 第一列(姓名)
_customListView.AddItemToColumn(0, "张三");
_customListView.AddItemToColumn(0, "李四");
_customListView.AddItemToColumn(0, "王五");
_customListView.AddItemToColumn(0, "赵六");
_customListView.AddItemToColumn(0, "孙七");
_customListView.AddItemToColumn(0, "周八");
_customListView.AddItemToColumn(0, "吴九");
// 第二列(年龄)
_customListView.AddItemToColumn(1, "25");
_customListView.AddItemToColumn(1, "30");
_customListView.AddItemToColumn(1, "28");
_customListView.AddItemToColumn(1, "35");
_customListView.AddItemToColumn(1, "22");
_customListView.AddItemToColumn(1, "40");
_customListView.AddItemToColumn(1, "27");
// 第三列(城市)
_customListView.AddItemToColumn(2, "北京");
_customListView.AddItemToColumn(2, "上海");
_customListView.AddItemToColumn(2, "广州");
_customListView.AddItemToColumn(2, "深圳");
_customListView.AddItemToColumn(2, "杭州");
_customListView.AddItemToColumn(2, "成都");
_customListView.AddItemToColumn(2, "武汉");
// 设置位置和大小
_customListView.Width = 300;
_customListView.Dock = DockStyle.Top;
// 添加到窗体
this.Controls.Add(_customListView);
// 启动按钮
Button btnStartScroll = new Button();
btnStartScroll.Text = "开始滚动";
btnStartScroll.Location = new Point(10, 200);
btnStartScroll.Click += (s, e) => _customListView.StartScroll();
this.Controls.Add(btnStartScroll);
btnStartScroll.Dock=DockStyle.Bottom;
// 停止按钮
Button btnStopScroll = new Button();
btnStopScroll.Text = "停止滚动";
btnStopScroll.Location = new Point(100, 200);
btnStopScroll.Click += (s, e) => _customListView.StopScroll();
this.Controls.Add(btnStopScroll);
btnStopScroll.Dock = DockStyle.Bottom;
}
}
}
控件的绘制逻辑通过重写 OnPaint
方法实现。该方法使用 GDI+ 的 Graphics
对象进行自定义绘制,以下是该方法的主要步骤:
CustomScrollListView
为需要展示多列数据的 WinForms 应用程序提供了一个灵活的解决方案。这种控件通过自定义绘制和使用 GDI+ 提供了平滑的视觉效果,并易于扩展与集成。使用此控件,开发者可以创造出更加动感和用户友好的界面响应。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!