AutoRedraw屬性與窗體設備場景的深入探討
發表于:2007-07-14來源:作者:點擊數:
標簽:
大家知道AutoRedraw是設置窗體是否重畫的。那為什么為false窗體上的圖形就不會被重畫,而為true則窗體在被覆蓋后再顯示,窗體上的圖形仍舊在? 這個問題其實與窗體的設備場景有直接的關系。即使沒接觸過 Windows GDI 開發 的朋友也一定聽說過設備場景(Devic
大家知道AutoRedraw是設置窗體是否重畫的。那為什么為false窗體上的圖形就不會被重畫,而為true則窗體在被覆蓋后再顯示,窗體上的圖形仍舊在?
這個問題其實與窗體的設備場景有直接的關系。即使沒接觸過
Windows GDI
開發的朋友也一定聽說過設備場景(Device Context),設備場景是用來控制窗體圖形顯示方式的對象。比如,我們可以通過設置設備場景的畫筆對象來控制在窗體上的線條的顏色、粗細,設置區域對象來控制窗體的外形,比如說將窗體變成五角形,星形,甚至任何亂七八糟的形狀,可以設置剪切區并加一點代碼就可以將窗體變成透明的,或者通過CBitmap對象來截獲窗體的圖象——而我們今天討論的是如何在AutoRedraw屬性的兩種設置下返回我們所需的設備場景句柄。
窗體有這么幾個屬性需要復習一下:hDC——窗體的設備場景;窗體image對象的Handle屬性——窗體前景畫面圖像的句柄;窗體picture對象的Handle屬性——窗體背景圖像的句柄。當我們將窗體的AutoRedraw屬性設置為true時,窗體的設備場景其實存在兩個。這可以以以下代碼證實:
Private Sub Form_Click()
Dim lDC As Long
lDC = GetDC(Me.hwnd) ‘GetDC是一個API函數,它返回某窗體的設備場景。請用API Text Viewer加入該函數的聲明
Print Me.hDC
Print lDC
End Sub
(注意當窗體的AutoRedraw屬性為true時窗體的Paint事件不會被激發。)
在窗體上輸出的數字中我們清析地看到,這兩個句柄是不同的。其中,窗體的hDC屬性是窗體的一個內存設備場景句柄。對于窗體的任何繪圖(例如畫一條線,一個圓圈等)工作都會寫入該內存設備場景。每當該窗體接收到一個Paint事件時,它就用該內存設備場景刷新窗體。所以當我們重新顯示窗體的時候,它始終能重畫窗體上所有的圖形。而我們用GetDC函數返回的設備場景句柄則是真正意義上的窗體句柄,當前窗體顯示什么圖形,那么該設備場景的CBitmap對象保存的就是該圖形。比如說,我們畫了一個圓圈,又用別的窗體將它掩蓋了一半,我們又用GetDC返回的句柄截獲窗體的圖片,那么該圖片中只有一半的圓圈。
當AutoRedraw屬性為false的時候,窗體的內存設備場景就不存在了。由于沒有了保存窗體圖像信息的內存設備場景,所以窗體就無法確定重顯時如何重畫窗體,因而就不會去重畫被覆蓋掉又重顯的內容。用GetDC函數和窗體的hDC屬性返回的句柄是相同的,它們都是窗體真正意義上的設備場景句柄。
以下語句可以截取窗體的圖像,大家可以在AutoRedraw屬性為不同的時候截獲這幾種可能的窗體圖片以比較一下。(請先在窗體上畫一個PictureBox,取名為picSave,并將其Visible屬性設為false,AutoRedraw屬性設為true。再將窗體及圖像框的ScaleMode設為3。運行該程序前請先檢查PictureBox的hDC是否存在,小弟在幾臺機子上發現即使設置了HasDC屬性仍無法獲得該hDC的情況。)
如小弟有錯誤之處歡迎來信探討:sproll@163.com
Option Explicit
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, _
ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, _
ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long
Private Const SR
CCOPY = &HCC0020
Private Sub Form_Click()
Dim lReturn As Long
Dim lDC As Long
Dim lBitmap As Long
Dim lOldBitmap As Long
lDC = CreateCompatibleDC(frmAutoRedraw.PicSave.hdc) '創建與PictureBox的設備場景兼容的一內存設備場景
lBitmap = CreateCompatibleBitmap(frmAutoRedraw.hdc, frmAutoRedraw.ScaleWidth, frmAutoRedraw.ScaleHeight) '創建與窗體設備場景的CBitmap對象兼容的CBitmap對象
lOldBitmap = SelectObject(lDC, lBitmap) '將創建的新Cbitmap對象選入內存設備場景,并保存原CBitmap對象。
lReturn = BitBlt(lDC, 0, 0, frmAutoRedraw.ScaleWidth, frmAutoRedraw.ScaleHeight, frmAutoRedraw.hdc, 0, 0, SRCCOPY) '將窗體上顯示的圖樣拷貝到內存設備場景
lReturn = BitBlt(frmAutoRedraw.PicSave.hdc, 0, 0, frmAutoRedraw.ScaleWidth, frmAutoRedraw.ScaleHeight, lDC, 0, 0, SRCCOPY) '將內存設備場景的圖樣拷貝到PictureBox的設備場景中
SavePicture frmAutoRedraw.PicSave.Image, "c:\My documents\form.bmp" '保存PictureBox的圖樣
lBitmap = SelectObject(lDC, lOldBitmap) '將內存設備場景的原CBitmap對象選回內存設備場景
lReturn = DeleteObject(lBitmap) '刪除創建的CBitmap對象
lReturn = DeleteDC(lDC) '刪除內存設備場景
End Sub
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button =
vbRightButton Then
frmAutoRedraw.Line (0, 0)-(150, 150)
End If
End Sub
Private Sub Form_Resize()
PicSave.Width = Me.ScaleWidth
PicSave.Height = Me.ScaleHeight
End Sub
原文轉自:http://www.anti-gravitydesign.com