如何改进在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();
}
}
}
与其在面板的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
祝你好运!