Existing Graphics into Bitmap(将现有图形转换为位图)
问题描述
我正在为交易软件(C#、winforms、.NET 3.5)编写一个插件,我想在包含数据的面板(比如 ChartPanel
)上绘制一个十字光标画画可能很贵.到目前为止,我所做的是:
I'm writing a plugin for a trading software (C#, winforms, .NET 3.5) and I'd like to draw a crosshair cursor over a panel (let's say ChartPanel
) which contains data that might be expensive to paint. What I've done so far is:
- 我在面板中添加了一个
CursorControl
- 此
CursorControl
位于主绘图面板上方,以便覆盖整个区域 - 它是
Enabled = false
以便所有输入事件都传递给父级图表面板
- 实现了它的
Paint
方法,以便它在当前鼠标位置从上到下和从左到右绘制线条
- 此
- I added a
CursorControl
to the panel- this
CursorControl
is positioned over the main drawing panel so that it covers it's entire area - it's
Enabled = false
so that all input events are passed to the parentChartPanel
- it's
Paint
method is implemented so that it draws lines from top to bottom and from left to right at current mouse position
- this
- A) 调用
ChartPanel.Invalidate()
,但正如我所说,底层数据的绘制可能很昂贵,这会导致每次移动鼠标时都重新绘制所有内容,这是错误的(但是这是我现在可以完成这项工作的唯一方法) - B) 调用
CursorControl.Invalidate()
并且在绘制光标之前我会拍摄当前绘制数据的快照并将其作为光标的背景,每次光标都会恢复需要重新粉刷……问题是……我不知道该怎么做.
- A) Call
ChartPanel.Invalidate()
, but as I said, the underlying data may be expensive to paint and this would cause everything to redraw everytime I move a mouse, which is wrong (but it is the only way I can make this work now) - B) Call
CursorControl.Invalidate()
and before the cursor is drawn I would take a snapshot of currently drawn data and keep it as a background for the cursor that would be just restored everytime the cursor needs to be repainted ... the problem with this is ... I don't know how to do that.
2.B.意思是:
- 将现有的
Graphics
对象转换为Bitmap
(它(图形)是通过 Paint 方法提供给我的,我必须在它上面作画,所以我只是无法创建新的 Graphics 对象......也许我理解错了,但我是这么理解的) - 在绘制十字准线之前,从位图中恢复图形内容并重新绘制十字准线
- Turn existing
Graphics
object intoBitmap
(it (the Graphics) is given to me through Paint method and I have to paint at it, so I just can't create a new Graphics object ... maybe I get it wrong, but that's the way I understand it) - before the crosshair is painted, restore the Graphics contents from the Bitmap and repaint the crosshair
我无法控制绘制昂贵数据的过程.我只能访问我的 CursorControl
以及通过 API 调用的方法.
I can't control the process of painting the expensive data. I can just access my CursorControl
and it's methods that are called through the API.
那么有没有什么办法可以把已有的Graphics内容存入Bitmap中,以后再恢复呢?或者有什么更好的方法可以解决这个问题?
So is there any way to store existing Graphics contents into Bitmap and restore it later? Or is there any better way to solve this problem?
已解决:经过数小时的反复试验,我想出了一个可行的解决方案.我用的软件有很多问题不能一概而论,但主要的原则是明确的:
RESOLVED: So after many hours of trial and error I came up with a working solution. There are many issues with the software I use that can't be discussed generally, but the main principles are clear:
- 已经绘制的现有图形不能直接转换为位图,而是我必须使用@Gusman 的回答中首先提到的
panel.DrawToBitmap
方法.我知道,我想避免它,但最终我不得不接受,因为它似乎是唯一的方法 - 我还想避免每一帧都重复绘制,所以第一个十字线绘制总是直接绘制到
ChartPanel
.在鼠标移动而不更改图表图像后,我通过DrawToBitmap
进行快照并按照所选答案中的描述进行操作. - 控件必须是不透明的(未启用透明背景),以便刷新它不会在其父控件上调用 Paint(这会导致整个图表重新绘制)
- existing Graphics with already painted stuff can't be converted to Bitmap directly, instead I had to use
panel.DrawToBitmap
method first mentioned in @Gusman's answer. I knew about it, I wanted to avoid it, but in the end I had to accept, because it seems to be the only way - also I wanted to avoid double drawing of every frame, so the first crosshair paint is always drawn directly to the
ChartPanel
. After the mouse moves without changing the chart image I take a snapshow throughDrawToBitmap
and proceed as described in chosen answer. - The control has to be Opaque (not enabled Transparent background) so that refreshing it doesn't call Paint on it's parent controls (which would cause the whole chart to repaint)
我仍然每隔几秒左右就会偶尔出现闪烁,但我想我可以以某种方式解决这个问题.虽然我选择了 Gusman 的答案,但我要感谢所有参与的人,因为我使用了其他答案中提到的许多其他技巧,例如 Panel.BackgroundImage、使用 Plot() 方法而不是 Paint() 来锁定图像等.
I still experience occasional flicker every few seconds or so, but I guess I can figure that out somehow. Although I picked Gusman's answer, I would like to thank everyone involved, as I used many other tricks mentioned in other answers, like the Panel.BackgroundImage, use of Plot() method instead of Paint() to lock the image, etc.
推荐答案
为什么不将 ChartPanel 中的所有图形克隆到 CursorControl 上?
Why don't you clone all the graphics in the ChartPanel over your CursorControl?
此处的所有代码都必须放在 CursorControl 中.
All the code here must be placed inside your CursorControl.
首先,创建一个属性,该属性将保存对图表的引用并与其绘制事件挂钩,如下所示:
First, create a property which will hold a reference to the chart and hook to it's paint event, something like this:
ChartPanel panel;
public ChartPanel Panel
{
get{ return panel; }
set{
if(panel != null)
panel.Paint -= CloneAspect;
panel = value;
panel.Paint += CloneAspect;
}
}
现在定义 CloneAspect 函数,该函数将在图表面板中完成绘制操作时将控件的外观呈现为位图:
Now define the CloneAspect function which will render the control's appearance to a bitmap whenever a Paint opperation has been done in the Chart panel:
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 重写方法中执行以下操作:
Then in the OnPaint overriden method do this:
public override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(aspect);
//Now draw the cursor
(...)
}
最后,无论您在何处创建图表和自定义光标:
And finally wherever you create the chart and the customcursor you do:
CursorControl.Panel = ChartPanel;
瞧,您可以根据需要重新绘制多次,而无需重新计算图表的内容.
And voila, you can redraw as many times you need without recalculating the chart's content.
干杯.
这篇关于将现有图形转换为位图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:将现有图形转换为位图
基础教程推荐
- C# - 将浮点数转换为整数...并根据余数更改整数 2022-01-01
- 当键值未知时反序列化 JSON 2022-01-01
- 覆盖 Json.Net 中的默认原始类型处理 2022-01-01
- 使用 SED 在 XML 标签之间提取值 2022-01-01
- Page.OnAppearing 中的 Xamarin.Forms Page.DisplayAlert 2022-01-01
- 创建属性设置器委托 2022-01-01
- 从 VB6 迁移到 .NET/.NET Core 的最佳策略或工具 2022-01-01
- 如何使用OpenXML SDK将Excel转换为CSV? 2022-01-01
- C# - 如何列出发布到 ASPX 页面的变量名称和值 2022-01-01
- 我什么时候应该使用 GC.SuppressFinalize()? 2022-01-01