我正在编写一个交易软件插件(C#,winforms,.NET 3.5),我想在一个面板上画一个十字光标(让我们说{{1} })包含绘制可能很昂贵的数据。到目前为止我所做的是:
ChartPanel
CursorControl
位于主绘图面板上方,以便覆盖整个区域CursorControl
以便将所有输入事件传递给父级
Enabled = false
ChartPanel
方法,以便在当前鼠标位置从上到下,从左到右绘制线条Paint
,但正如我所说,绘制底层数据可能很昂贵,这会导致每次移动鼠标时都重绘一次,这是错误的(但这是我唯一的方法现在就做这个工作)ChartPanel.Invalidate()
,在绘制光标之前,我会拍摄当前绘制数据的快照,并将其保留为光标的背景,每次光标需要重新绘制时光标都会恢复...这个问题是...... 我不知道该怎么做。2.B。意思是:
CursorControl.Invalidate()
对象转换为Graphics
(它(图形)通过Paint方法提供给我,我必须在其上绘画,所以我无法创建一个新的Graphics对象...也许我弄错了,但这就是我理解它的方式) 我无法控制绘制昂贵数据的过程。我可以访问通过API调用的Bitmap
及其方法。
那么有没有办法将现有的Graphics内容存储到Bitmap中并在以后恢复?或者有更好的方法来解决这个问题吗?
决议:经过数小时的反复试验后,我想出了一个有效的解决方案。我使用的软件存在许多问题,无法对其进行一般性讨论,但主要原则很明确:
CursorControl
方法。我知道它,我想避免它,但最后我不得不接受,因为它似乎是唯一的方式panel.DrawToBitmap
。在鼠标移动而不更改图表图像后,我会通过ChartPanel
进行快照,然后按照所选答案中的说明继续操作。我每隔几秒左右仍会遇到偶尔的闪烁,但我想我能以某种方式解决这个问题。虽然我选择了Gusman的答案,但我要感谢所有参与者,因为我使用了其他答案中提到的许多其他技巧,比如Panel.BackgroundImage,使用Plot()方法代替Paint()来锁定图像等等。
答案 0 :(得分:3)
这可以通过多种方式完成,始终将图形存储为Bitmap
。最直接有效的方法是让Panel
为您完成所有工作。
以下是这个想法:大多数winforms Controls
都有两层显示。
在Panel
的情况下,两个图层是BackgroundImage
及其Control
表面。
许多其他控件也是如此,例如Label, CheckBox, RadioButton
或Button
。
(一个有趣的例外是PictureBox
,此外还有一个(前景)Image
。)
因此我们可以将昂贵的东西移到BackgroundImage
并在surcafe上绘制十字准线。
在我们的情况下,Panel
,所有不错的附加内容都已到位,您可以选择BackgroundImageLayout
属性的所有值,包括Tile, Stretch, Center
或Zoom
。我们选择None
。
现在我们为您的项目添加一个标志:
bool panelLocked = false;
以及根据需要设置的功能:
void lockPanel( bool lockIt)
{
if (lockIt)
{
Bitmap bmp = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Width);
panel1.DrawToBitmap(bmp, panel1.ClientRectangle);
panel1.BackgroundImage = bmp;
}
else
{
if (panel1.BackgroundImage != null)
panel1.BackgroundImage.Dispose();
panel1.BackgroundImage = null;
}
panelLocked = lockIt;
}
在这里,您可以看到工作中的魔力:在我们实际锁定Panel
做昂贵的事情之前,我们告诉它创建其图形的快照并将其放入BackgroundImage
..
现在我们需要使用该标志来控制Paint
事件:
private void panel1_Paint(object sender, PaintEventArgs e)
{
Size size = panel1.ClientSize;
if (panelLocked)
{
// draw a full size cross-hair cursor over the whole Panel
// change this to suit your own needs!
e.Graphics.DrawLine(Pens.Red, 0, mouseCursor.Y, size.Width - 1, mouseCursor.Y);
e.Graphics.DrawLine(Pens.Red, mouseCursor.X, 0, mouseCursor.X, size.Height);
}
// expensive drawing, you insert your own stuff here..
else
{
List<Pen> pens = new List<Pen>();
for (int i = 0; i < 111; i++)
pens.Add(new Pen(Color.FromArgb(R.Next(111),
R.Next(111), R.Next(111), R.Next(111)), R.Next(5) / 2f));
for (int i = 0; i < 11111; i++)
e.Graphics.DrawEllipse(pens[R.Next(pens.Count)], R.Next(211),
R.Next(211), 1 + R.Next(11), 1 + R.Next(11));
}
}
最后,我们编写MouseMove
的{{1}}脚本:
Panel
使用第二个类级变量:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
mouseCursor = e.Location;
if (panelLocked) panel1.Invalidate();
}
您可以根据需要致电 Point mouseCursor = Point.Empty;
或lockPanel(true)
。
如果直接实现此功能,您会发现一些闪烁。如果你使用双缓冲lockPanel(false)
:
Panel
这会以十分平滑的方式将十字准线移动到class DrawPanel : Panel
{
public DrawPanel() { this.DoubleBuffered = true; }
}
上方。你可能想打开&amp;关闭鼠标光标Panels
和MouseLeave
..
答案 1 :(得分:2)
为什么不通过CursorControl克隆ChartPanel中的所有图形?
此处的所有代码都必须放在CursorControl中。
首先,创建一个属性,该属性将保存对图表的引用并挂钩它的paint事件,如下所示:
ChartPanel panel;
public ChartPanel Panel
{
get{ return panel; }
set{
if(panel != null)
panel.Paint -= CloneAspect;
panel = value;
panel.Paint += CloneAspect;
}
}
现在定义CloneAspect函数,只要在Chart面板中完成Paint操作,它就会将控件的外观呈现给位图:
Bitmap aspect;
void CloneAspect(object sender, PaintEventArgs e)
{
if(aspect == null || aspect.Width != panel.Width || aspect.Height != panel.Height)
{
if(aspect != null)
aspect.Dispose();
aspect = new Bitmap(panel.Width, panel.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
}
panel.DrawToBitmap(aspect, new Rectangle(0,0, panel.Width, panel.Height);
}
然后在OnPaint overriden方法中执行以下操作:
public override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(aspect);
//Now draw the cursor
(...)
}
最后,无论您在何处创建图表和自定义光标,都可以:
CursorControl.Panel = ChartPanel;
瞧,您可以在不重新计算图表内容的情况下重绘您需要的次数。
干杯。