如何改进在c#中绘制大量组件的平移和缩放

本文关键字:组件 缩放 何改进 绘制 | 更新日期: 2023-09-27 18:09:54

最近我一直在学习和尝试用Winforms Panel绘制,缩放和平移。

我有一个双缓冲面板,我画了很多形状,当我平移形状时,它仍然闪烁,我想改进它,我想到的一个想法是将所有这些形状转换为一个bmp图像,所以我不需要重新绘制所有组件,我不确定这是否是最好的主意或任何人可以帮助我更好的方法。下面是代码

的一小段
public PanelViewer()
    {
        InitializeComponent();
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
        zoom = zoomTrackPad.Value / 10f;
    }
    public CircuitData ResistorData { get; set; }
    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.TranslateTransform(imgx, imgy);
        g.ScaleTransform(zoom, zoom);
        g.SmoothingMode = SmoothingMode.AntiAlias;
        SolidBrush myBrush = new SolidBrush(Color.Black);
        Pen p = new Pen(Color.Red);
        foreach (CircuitData.ResistorRow resistorRow in ResistorData.Resistor)
        {
            RectangleF rec = new RectangleF((float)(resistorRow.CenterX  - resistorRow.Length/ 2), (float)(resistorRow.CenterY - resistorRow.Width/ 2), (float)resistorRow.Length, (float)resistorRow.Width);
            float orientation = 360 - (float)resistorRow.Orientation;
            PointF center = new PointF((float)resistorRow.CenterX, (float)resistorRow.CenterY);
            PointF[] points = CreatePolygon(rec, center, orientation);
            if (!Double.IsNaN(resistorRow.HiX) && !Double.IsNaN(resistorRow.HiY))
            {
                g.FillEllipse(myBrush, (float)resistorRow.HiX  - 2 , (float)resistorRow.HiY - 2, 4, 4);
                g.DrawLine(p, new PointF((float)resistorRow.HiX , (float)resistorRow.HiY ), center);
            }
            g.FillPolygon(myBrush, points);
        }
    }
    private PointF[] CreatePolygon(RectangleF rec, PointF center, float orientation)
    {
        PointF TL = new PointF(rec.Left, rec.Top);
        PointF TR = new PointF(rec.Right, rec.Top);
        PointF BL = new PointF(rec.Left, rec.Bottom);
        PointF BR = new PointF(rec.Right, rec.Bottom);
        PointF[] points = new PointF[] { BL, TL, TR, BR, BL };
        System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix();
        matrix.RotateAt(orientation, center);
        matrix.TransformPoints(points);
        return points;
    }
    private void trackBar1_Scroll(object sender, EventArgs e)
    {
        zoom = zoomTrackPad.Value / 10f;
        Viewer.Invalidate();
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        MouseEventArgs mouse = e as MouseEventArgs;
        if (mouse.Button == MouseButtons.Left)
        {
            mouseDown = mouse.Location;
            startx = imgx;
            starty = imgy;
        }
    }
    protected override void OnMouseUp(MouseEventArgs e)
    {
        Cursor = Cursors.Default;
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Cursor = Cursors.Hand;
            MouseEventArgs mouse = e as MouseEventArgs;
            if (mouse.Button == MouseButtons.Left)
            {
                Point mousePosNow = mouse.Location;
                int deltaX = mousePosNow.X - mouseDown.X; // the distance the mouse has been moved since mouse was pressed
                int deltaY = mousePosNow.Y - mouseDown.Y;
                imgx = (int)(startx + (deltaX / zoom));  // calculate new offset of image based on the current zoom factor
                imgy = (int)(starty + (deltaY / zoom));
                Viewer.Refresh();
            }
        }
    }

如何改进在c#中绘制大量组件的平移和缩放

与其在面板的Paint事件上绘图,一个更好的方法(但有点复杂)是创建一个用户控件,将其配置为完全由用户绘制并覆盖OnPaint方法。

你看到的闪烁是因为控件正在绘制两个阶段,PaintBackground和paint,而且它正在浪费时间,因为它正在绘制不需要的内容(原始油漆)。

要创建一个用户绘制的控件,创建一个新的用户控件,并在构造函数中添加:

SetStyle(ControlStyles.AllPaintingInWmPaint, true); //do not use PaintBackground
SetStyle(ControlStyles.DoubleBuffer, true); //enable double buffer
SetStyle(ControlStyles.UserPaint, true); //all paint will be done by the user

然后重写OnPaint方法并绘制:

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.Clear(this.BackColor); //Important!! the control will not be cleared by itself, you must clear it
        //Render your content
    }

你还谈到只渲染需要的区域,这总是一个好主意OnPaint方法已经为此做好了准备,在参数中你有一个名为Clipping Rectangle的矩形,它的区域必须重新绘制,所以不是清除所有的控件而是清除这个矩形的区域,它与你的内容的所有区域相交(例如在你的代码中,你有

RectangleF rec = new RectangleF((float)(resistorRow.CenterX  - resistorRow.Length/ 2), (float)(resistorRow.CenterY - resistorRow.Width/ 2), (float)resistorRow.Length, (float)resistorRow.Width);
        float orientation = 360 - (float)resistorRow.Orientation;

,这将是组件的面积)并重新绘制相交的部分。

如果你需要更多关于用户绘制控件的信息MSDN有一个很好的部分:https://msdn.microsoft.com/en-us/library/kxys6ytf%28v=vs.85%29.aspx

祝你好运!