標籤:
原文網址:http://www.cnblogs.com/skywatcher/p/3572311.html
1.更新視窗
即採用UpdateWindow()函數立即發送WM_PAINT訊息更新整個視窗。
void CEditTestDlg::OnBnClickedBtnSysUpdate(){ CString csTmp; int i = 0; while (i < 100) { Sleep(20); i += 1; csTmp.Format(_T("%d"),i); m_value = csTmp;//無法更新只顯示結果 //m_editNum.SetWindowText(csTmp);//無法更新也不顯示結果 UpdateData(FALSE); UpdateWindow();//能更新但再次操作會卡死 //Invalidate(FALSE);//無法更新只顯示結果 //RedrawWindow(NULL,NULL,RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);//RDW_INVALIDATE和Invalidate()效果一樣,RDW_ERASE導致閃爍 }}
但是這會導致介面假死,更新過程中無法對視窗進行任何操作。控制項的Control變數有時候無法更新Invalidate()一般都在多文檔對話方塊裡使用,RedrawWindow()是前兩個都會去調用的函數,方式更多。詳情見:MFC重繪函數
2. 定時器實現
在需要更新的位置調用SetTimer()啟動定時器,在定時器響應函數OnTimer()裡面來處理更新,可以同時啟用多個定時器更新多個位置。
啟動定時器:
void CEditTestDlg::OnBnClickedBtnTimerUpdate(){ this->SetTimer(1,1,NULL); this->SetTimer(2,1,NULL);}
定時器內部處理:
void CEditTestDlg::OnTimer(UINT_PTR nIDEvent){ switch (nIDEvent) { case 1: if (tmp >= 100) { this->KillTimer(1); MessageBox(_T("定時器1停止!"), NULL, NULL); return; } Sleep(20); tmp += 1; m_value.Format(_T("%d"),tmp); UpdateData(FALSE); break; case 2: if (tmp1 >= 200) { this->KillTimer(2); MessageBox(_T("定時器2停止!"), NULL, NULL); return; } Sleep(20); tmp1 += 1; m_value2.Format(_T("%d"),tmp1); UpdateData(FALSE); default: break; } CDialog::OnTimer(nIDEvent);}
通過定時器介面不會假死,實現也簡單明了。
3. 線程更新
建立一個新的線程來更新介面,建立線程:
void CEditTestDlg::OnBnClickedBtnThreadUpdate(){ CWinThread* pThread; pThread = AfxBeginThread(UpdateThread,this);}
線程內部實現:
static UINT UpdateThread(LPVOID lpParam){ CEditTestDlg *dlg = (CEditTestDlg*) lpParam; int i = 0; while (i < 200) { Sleep(20); i += 1; dlg->m_value2.Format(_T("%d"), i); //dlg->UpdateData(FALSE);//release下可行 dlg->m_editCtl.SetWindowText(dlg->m_value2);//使用control變數 //dlg->GetDlgItem(IDC_EDIT2)->SetWindowText(dlg->m_value2); } return 0;}
在這裡,最好不要用UpdateData(FALSE)來顯示到介面,在Debug下回奔潰,而Release模式下沒有任何問題,具體見:線程調用UpdateData函數出錯
4. 線程中發送自訂訊息更新
線上程中給視窗發送自訂的更新介面的訊息,讓訊息加入系統訊息佇列達到更新介面的目的。自訂訊息可參照:MFC添加自訂訊息
void CEditTestDlg::OnBnClickedBtnMsgUpdate(){ CWinThread* pThread; pThread = AfxBeginThread(SendMsgThread,this);}
static UINT SendMsgThread(LPVOID lpParam){ CEditTestDlg *dlg = (CEditTestDlg*) lpParam; int i = 0; while (i < 100) { Sleep(20); i += 1; dlg->m_value2.Format(_T("%d"), i); //PostMessage(dlg->m_hWnd,WM_UPDATEDATA,FALSE,NULL); //SendMessage(dlg->m_hWnd,WM_UPDATEDATA,FALSE,NULL); SendMessageTimeout(dlg->m_hWnd, WM_UPDATEDATA, FALSE,NULL, SMTO_BLOCK, 1000, NULL); } return 0;}
發送訊息的時候PostMessage(),SendMessage()和SendMessageTimeout()都可以實現,具體區別見:SendMessage和PostMessage區別
5. 裝置繪製更新
直接在介面中不停的繪製變化的部分,這種方法也會造成假死。
void CEditTestDlg::OnBnClickedBtnDrawUpdate(){ CString csTmp; int i = 0; CDC* pDC = this->GetDC(); while (i < 100) { Sleep(20); i += 1; csTmp.Format(_T("%d"),i); pDC->TextOut(20, 100, csTmp); } ReleaseDC(pDC);}
6. STATIC控制項更新
給STATIC控制項賦值更新,同樣會造成假死現象。(值得一提的是以前在VC6.0中可以對EIDT控制項使用同樣的方法更新而現在去測試卻不行,希望有經驗的人給予指導。)
void CEditTestDlg::OnBnClickedBtnStaticUpdate(){ CString csTmp; int i = 0; while (i < 100) { Sleep(20); i += 1; csTmp.Format(_T("%d"),i); m_csStaticNum = csTmp;//能更新但再次操作會卡死 //m_staticCtlNum.SetWindowText(csTmp);//不更新也不顯示結果 UpdateData(FALSE); }}
7. 發送系統訊息更新
直接給系統訊息迴圈發送WM_PAINT訊息來更新介面,但是同樣會出現假死情況,所以可以建立一個函數DoEvent()實現:在有系統訊息,如拖動點擊等訊息時,暫停處理我們發送的訊息而優先去處理系統訊息。
void CEditTestDlg::OnBnClickedBtnMsg(){ CString csTmp; int i = 0; while (i < 100) { Sleep(20); i += 1; csTmp.Format(_T("%d"),i); m_value = csTmp;//能更新但再次操作會卡死 //m_editNum.SetWindowText(csTmp);//無法更新也不顯示結果 UpdateData(FALSE); GetDlgItem(IDC_EDIT1)->SendMessage(WM_PAINT);//能更新但再次操作會卡死 DoEvents();//轉出處理系統訊息 }}
//有系統訊息時暫停處理系統訊息void DoEvents(){ MSG msg; //定義一個MSG類型的變數 while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) //擷取訊息並把該訊息從訊息佇列中移除(防止重複響應)。 { TranslateMessage(&msg);//翻譯訊息 在合適的機會產生char訊息 DispatchMessage(&msg); //將訊息移交給過程函數 }}
但是出現的問題是,我們在拖動對話方塊的時候,介面停止了更新。
所以比較而言,線程發送訊息和定時器效果相對較好,介面不會假死,在進行拖動等操作的時候也不會出現停止等待的現象。
【轉】MFC介面更新實現方法