带AutoScroll的UserControl:AutoScrollPosition处理错误致标签显示异常
解决自动滚动UserControl中标签渲染偏移/消失问题
问题根源
你的代码存在三个核心问题导致滚动时标签显示异常:
- AutoScrollPosition坐标处理错误:
AutoScrollPosition.Y在向下滚动时为负数,直接用rect.Y - AutoScrollPosition.Y会反向偏移,应该用加法修正坐标。 - DrawLabel额外添加padding导致位置错位:存储的标签矩形已经包含了初始偏移(20px),但DrawLabel里又叠加了padding,导致标签实际绘制位置偏离预期。
- 绘制区域与可见性判断不匹配:可见性判断用的是修正后的
shiftedRect,但实际绘制时又叠加了padding,导致判断可见的标签绘制到了错误区域。
修正后的完整代码
public class LabelListController : UserControl { int stringNumber = 0; List<Rectangle> labels = new List<Rectangle>(); public LabelListController() { this.DoubleBuffered = true; this.AutoScroll = true; this.BackColor = ColorTranslator.FromHtml("#1B1F3B"); this.Dock = DockStyle.Fill; InitializeButton(); } private void InitializeButton() { Button plusButton = new Button() { Text = "+", BackColor = ColorTranslator.FromHtml("#2B2B2B"), FlatStyle = FlatStyle.Flat, Size = new Size(30, 30), Location = new Point(this.Width - 50, 10), Anchor = AnchorStyles.Top | AnchorStyles.Right }; plusButton.FlatAppearance.BorderSize = 0; this.Controls.Add(plusButton); plusButton.Click += PlusButton_Click; } private void PlusButton_Click(object sender, EventArgs e) { this.stringNumber++; int newY = 20 + (stringNumber - 1) * 80; labels.Add(new Rectangle(20, newY, 100, 60)); this.AutoScrollMinSize = new Size(0, labels.Count * 80 + 40); this.Invalidate(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); // 修正AutoScrollPosition的坐标处理:滚动时Graphics的原点已偏移,需用加法修正 int scrollOffsetY = this.AutoScrollPosition.Y; foreach (var rect in labels) { // 计算实际绘制的矩形(考虑滚动偏移) Rectangle shiftedRect = new Rectangle(rect.X, rect.Y + scrollOffsetY, rect.Width, rect.Height); if (e.ClipRectangle.IntersectsWith(shiftedRect)) { // 直接传入修正后的位置,不再叠加额外padding DrawLabel(e, shiftedRect.Location, rect.Width, rect.Height); } } } private void DrawLabel(PaintEventArgs e, Point position, int sizeX, int sizeY) { // 移除额外的padding,因为存储的rect已经包含了初始偏移 Rectangle rect = new Rectangle(position.X, position.Y, sizeX, sizeY); float textX = rect.X + rect.Width / 2f; float textY = rect.Y + rect.Height / 2f; DrawRoundedRectangle( e.Graphics, rect, 15, ColorTranslator.FromHtml("#2A1E3C"), Color.White, 0 ); DrawString(e.Graphics, "Sample Text", textX, textY, 12); } private void DrawRoundedRectangle(Graphics g, Rectangle bounds, int cornerRadius, Color fillColor, Color borderColor, float borderWidth) { using (GraphicsPath path = new GraphicsPath()) { int diameter = cornerRadius * 2; path.AddArc(bounds.X, bounds.Y, diameter, diameter, 180, 90); path.AddArc(bounds.Right - diameter, bounds.Y, diameter, diameter, 270, 90); path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90); path.AddArc(bounds.X, bounds.Bottom - diameter, diameter, diameter, 90, 90); path.CloseFigure(); using (SolidBrush brush = new SolidBrush(fillColor)) { g.FillPath(brush, path); } if (borderWidth > 0) { using (Pen pen = new Pen(borderColor, borderWidth)) { pen.Alignment = PenAlignment.Center; g.DrawPath(pen, path); } } } } private void DrawString(Graphics g, string text, float x, float y, int textSize) { using (System.Drawing.Font drawFont = new System.Drawing.Font("Arial", textSize, FontStyle.Regular)) using (SolidBrush drawBrush = new SolidBrush(Color.White)) { StringFormat format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; PointF point = new PointF(x, y); g.DrawString(text, drawFont, drawBrush, point, format); } } }
关键修改说明
- 修正滚动坐标计算:将
shiftedY = rect.Y - this.AutoScrollPosition.Y改为rect.Y + this.AutoScrollPosition.Y,因为AutoScrollPosition的Y值在向下滚动时为负,加法才能正确抵消滚动带来的原点偏移。 - 移除DrawLabel中的padding:原来的
posX = position.X + paddingX和posY = position.Y + paddingY会导致标签位置额外偏移,直接使用传入的position即可,因为存储的labels列表中的Rect已经包含了初始的20px偏移。 - 保持可见性判断与绘制区域一致:现在
shiftedRect就是实际要绘制的区域,判断可见后直接传入该区域的位置进行绘制,确保逻辑统一。
内容的提问来源于stack exchange,提问作者Rei Ayanami
相关产品推荐
相关产品推荐

