You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带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);
        }
    }
}

关键修改说明

  1. 修正滚动坐标计算:将shiftedY = rect.Y - this.AutoScrollPosition.Y改为rect.Y + this.AutoScrollPosition.Y,因为AutoScrollPosition的Y值在向下滚动时为负,加法才能正确抵消滚动带来的原点偏移。
  2. 移除DrawLabel中的padding:原来的posX = position.X + paddingX和posY = position.Y + paddingY会导致标签位置额外偏移,直接使用传入的position即可,因为存储的labels列表中的Rect已经包含了初始的20px偏移。
  3. 保持可见性判断与绘制区域一致:现在shiftedRect就是实际要绘制的区域,判断可见后直接传入该区域的位置进行绘制,确保逻辑统一。

内容的提问来源于stack exchange,提问作者Rei Ayanami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:05:02