MFC 刷新失效的Picture控件


 

問題描述如在攝像頭顯示時,關閉攝像頭,此時Picture控件仍然顯示最后一幀圖像,需要刷新掉,還原Picture控件。或者重復顯示兩張不同大小的圖片時,第二張背景有第一張圖片殘留。

 

解決方法1:(最笨的方法)

 用對話框背景色來填充控件,

CRect rect;
GetDlgItem(IDC_ShowImage)->GetClientRect(&rect);
GetDlgItem(IDC_ShowImage)->GetDC()->FillSolidRect(&rect2, RGB(255, 255, 255));//可能會把邊界線也刷掉
//CRect rect2(rect.left+1 , rect.top+1 , rect.Width()-1 , rect.Height()-1 ); //可以轉化一下,但治標不治本

 

解決方法2:

(1)Invalidate(); //使整個窗體無效,全部重繪

(2)GetDlgItem(IDC_ShowImage)->Invalidate(true);//使控件部分無效,進行重繪,但測試發現好像無效,原因不知,有知道的可以留言告知,謝謝!

Invalidate()是使整個窗口客戶區無效, 窗口的客戶區無效意味着需要重繪,會將整個窗口下所有控件都給刷新,一般會造成閃爍

 

Invalidate標記一個需要重繪的無效區域,並不意味着調用該函數后就立刻進行重繪。類似於PostMessage(WM_PAINT),需要處理到WM_PAINT消息時才真正重繪。

Invalidate里面有個bool型的參數,用來標識重繪的時候是否用背景色填充,默認為true。

 

注意:UpdateWindow() 則是要求系統對區域進行立即重繪。向窗體發送WM_PAINT消息,在發送之前判斷GetUpdateRect(hWnd,NULL,TRUE)看有無可繪制的客戶區域,如果沒有,則不發送WM_PAINT。

設想了一下:直接UpdateWindow()是不執行的,因此,

InvalidateRect(rect); //聲明無效區
UpdateWindow(); //進行重繪

確實重繪了,但由於相對坐標系的問題,導致重繪區域不是想要的,方法3會講解原因以及解決方法。

UpdateData()這個函數不是刷新界面用的。
UpdateData()參數為FALSE時,將界面上控件綁定的變量的數據導到控件內,參數為TRUE時,導入方向則相反。

 

解決方法3:(比較推薦的)

InvalidateRect(rect);
InvalidateRect(rect,true);   //rect如果為NULL,全部的窗口客戶區域將被增加到更新區域中。
  rect是你指定要刷新的區域,此區域外的客戶區域不被重繪,這樣防止客戶區域的一個局部的改動,而導致整個客戶區域重繪而導致閃爍,如果最后的參數為TRUE,則還向窗體發送WM_ERASEBKGND消息,在客戶區域重繪之前先重繪背景。
如果希望立即刷新無效區域,可以在調用InvalidateRect之后調用UpdateWindow,
 
補充說明:
InvalidateRect是將窗口中的一塊矩形區域標注為“無效”,系統會不斷向窗口發送WM_PAINT消息令其重繪。在響應WM_PAINT消息時,需要調用BeginPaint獲取DC來進行重繪。該函數會合並所有“無效”區域,對DC進行裁剪,將整個窗口標注為“有效”,清除WM_PAINT消息。DC經裁剪之后,在進行繪制時,超出DC范圍的操作將不被處理,所以即使在響應WM_PAINT消息時繪制的是整個窗口,而實際上繪制的也只是“無效”區域。恰當地使用InvalidateRect進行刷新比刷新整個窗口的效率要高。在WM_PAINT消息時,應盡量根據PAINTSTRUCT結構中rcPaint指定的矩形來處理重繪,減少執行不必要的代碼,從而提高效率。調用InvalidateRect后不需要手動發送WM_PAINT消息。
 
但要特別注意的是,InvalidateRect(rect)  使用的是對話框的坐標系,而不是繪圖控件的坐標系。所以這種用法並不能直接得到想要的效果,
pWnd->GetClientRect(&rect);//得到控件客戶區區域

這是因為該種方法得到的 rect是控件客戶區坐標, 而InvalidateRect ,RedrawWindow用的卻是對話框客戶區坐標,所以在使用InvalidateRect、RedrawWindow 之前 必須將其他坐標系下的坐標進行轉換,否則不能按預想的執行。

CWnd* pWnd = GetDlgItem(IDC_ShowImage);
pWnd->GetClientRect(&rect);//得到控件客戶端區域坐標
pWnd->ClientToScreen(rect);//將區域坐標由 控件客戶區轉成對話框區
//pWnd->GetWindowRect(&rect); //直接得到控件的對話框區坐標

this->ScreenToClient(rect); //將區域坐標由 對話框區轉成對話框客戶區坐標
InvalidateRect(rect);
//RedrawWindow(rect);

這里面有幾個需要注意的點:

1.可以通過GetWindowRect直接獲取控件相對於對話框區坐標。

2. 注意對話框區和對話框客戶區的區別。(參考鏈接2)

3.在這里,InvalidateRect(rect);和RedrawWindow(rect)效果一樣,這是由於消息隊列消息很少,所以執行很快,但需要注意:

InvalidateRect()不能實現立即更新 ;

InvalidateRect()與 UpdateWindow()結合后可以實現立即更新;

兩者結合的效果相當於RedrawWindow(rect)。

 

參考鏈接:

(1) InvalidateRect()、UpdateWindow()、RedrawWindow()區別

  http://blog.csdn.net/shuilan0066/article/details/6826000

(2) GetWindowRect和GetClientRect的區別詳解

  http://www.cnblogs.com/wb-DarkHorse/archive/2013/07/08/3178201.html

(3) 使用InvalidateRect(rect) 防止圖片移動時發生閃爍

  http://blog.csdn.net/shuilan0066/article/details/6676229

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM