2012-01-25 95 views
2

我想讓自己瞭解一些WinForm圖形知識,所以我將我的小2D編輯器從XNA改寫爲WinForm Graphics-only。使用Winforms進行繪製

現在,我爲自己製作了一個用於tileset的新用戶控件,但正如我所見,Paint方法僅在控件初始化時調用。由於我想永久重畫我的控件(或至少通過MouseOver事件來節省一點性能),所以我聽說Invalidate()方法讓控件重繪本身,但這是方式太不合格。

有什麼辦法讓我的UserControl通過代碼繪製自己,而沒有這些性能問題?

回答

3

Paint方法是而不是只在控件初始化時調用。每次控件需要重新繪製時都會調用它。這當然是在控件第一次創建時發生的。當你的應用程序被最小化,然後被恢復時,當另一個窗口移過你的應用程序,遮住它的內容然後被移除時,也會發生這種情況,等等。當您使用Invalidate方法或同等方法使控件的客戶區失效時也會發生這種情況。這是在Windows開發早期作爲性能優化完成的 - 不需要重新繪製沒有改變的東西!

如果要強制重新繪製控件,應調用Invalidate method並指定要重新繪製的客戶區的特定區域。

我不知道你的意思是「那是方式太不合格了」。對於Invalidate方法來說,這是不可能的。它所做的只是設置一個標誌,告訴Windows你的控制需要在空閒時重新繪製(不處理任何其他消息)。

如果要強制Windows重新繪製控件立即(無需等待它是空閒的;內置於Windows從成立之初的另一個性能優化),調用Update method,這迫使所有無效立即重繪區域。

如果您的代碼在Paint事件處理程序方法內緩慢,唯一的方法可能會很慢。顯然,我不能告訴你如何優化代碼而不先看。


反正是有讓我的用戶通過代碼油漆本身,而無需這些性能問題?

Paint事件恰好是控件應該如何以及在哪裏繪製自己。這就是爲什麼它在那裏。

如果做的Paint事件不油漆,任何你畫該控件重繪(正如前面提到的,可以應對任何數量的發生的預期和非預期接下來的時間將被清除事件)。

但是,有時將臨時對象繪製到控件的客戶區域(例如響應MouseDown事件顯示拖動矩形)是有意義的。在這種情況下,您可以隨時獲得Graphics類的實例(它通常作爲參數傳遞給您的Paint事件處理程序方法,並在其上調用方法來執行繪圖)。您可以通過調用控件上的CreateGraphics method來完成此操作,該控件返回Graphics對象。然後,您可以繪製到Graphics對象上,就像在Paint事件處理程序方法中一樣。

顯然,這不能/不會出現任何超過Paint事件處理方法裏面的繪製代碼更快(如果這是事實上的罪魁禍首),但它會引起畫面立即更新而比每當控制空閒並且不處理任何其他消息。

我會再次重申,這種做法應該被使用立即提供和臨時反饋,因爲一切你畫在下一次重繪控件將被刪除。當發生這種情況時,會引發Paint事件,並且您的代碼在該方法處理程序中運行,它不知道您在其他一次性事件上繪製的內容。這就是爲什麼一切都應該發生在Paint事件處理程序方法內部,並且當某個其他事件需要重新繪製時,應該調用Invalidate(也可能通常不是,但通常不是Update)。

+0

首先 - 感謝您的回答。那麼,也許這不是一個性能問題,但我的MouseMove方法等待控件重新繪製的時間?我的控件中有一個ScrollY變量,它應該調整圖形的位置,並由其側面的vScrollbar進行調整。所以我有一個vScrollbar_Scroll-Event,它更新了ScrollY值並調用了滾動條的Invalidate-Method。但它只是非常緩慢。我究竟做錯了什麼? –

+0

好的,感謝您的建議,我進一步研究並解決了這個問題。我將ControlStyles.AllPaintingInWmPaint和ControlStyles.OptimizedDoubleBuffer標誌設置爲true,並減少了我的繪畫區域和一切工作非常流暢。謝謝! –

+1

@哈拉爾德:嗯,是的。雙緩衝是平滑UI更新的標準方法。通常它是針對閃爍指示的,而不是你所描述的「慢」。 「AllPaintingInWmPaint」意味着控件不會首先通過處理WM_ERASEBKGND消息並繪製默認顏色來消除自己。雙緩衝由WinForms提供。它首先將所有內容繪製到屏幕外的緩衝區中,然後將屏幕外緩衝區直接屏蔽到屏幕上。這很有幫助,因爲您看不到所有的中間繪畫步驟。 –