問題描述
我正在為交易軟件(C#、w??informs、.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.
干杯.
這篇關于將現有圖形轉換為位圖的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!