金君飛
在開發軟件時,總希望軟件界面越漂亮越好,我們在C++ Builder的SDI程序中比較容易實現漂亮的軟件界面,但在MDI程序中,由于Windows的MDI軟件的開發隱藏了許多技術細節,用SDI程序的方法,就不能實現了。下面筆者將詳細講述如何實現MDI程序背景圖。 在MDI程序中是由兩個窗口構成的,一個MDI主窗口和一個客戶窗口。客戶窗口覆蓋了主窗口的客戶區,并提供大量的MDI支持。在C++ Builder的MDI 的主窗口中提供了一個ClientHandle的客戶窗口句柄,我們就是利用這個句柄來實現位圖的顯示。我們知道,在Windows的窗口中繪置位圖,為使位圖能夠不斷地刷新,必須響應WM_PAINT和WM_ERASEBKGND消息。我們可以利用鉤子函數(鉤子是Windows系統中一種特殊的消息處理機制,可以監視系統或進程中的各種事件消息,截獲發往目標窗口的消息并進行處理)。截住Windows系統發送給MDI客戶窗口的WM_PAINT和WM_ERASEBKGND消息,從而實現客戶窗口的刷新和重繪。我們可以在系統中安裝自定義的消息鉤子,對發往客戶窗口的消息進行過濾,只處理WM_PAINT和WM_ERASEBKGND,以實現我們的目標。
編寫鉤子函數 編寫Windows鉤子函數分為三步:定義鉤子、安裝鉤子和卸載鉤子。 定義鉤子函數 鉤子函數是一種特殊的回調函數,不同事件的鉤子其函數頭是不一樣,本次用到的鉤子函數如下所示: LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) 參數nCode的值表示本鉤子函數是否必須處理該消息, wParam表明這次傳遞的消息是否已從Windows消息隊列中刪除, lParam參數用來傳送消息。 在鉤子函數中,必須將系統發送的消息繼續回送給系統以使其它程序可以繼續使用該消息,該函數為: LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam ) 參數hhk是安裝鉤子函數時安裝函數返回的句柄,nCode、wParm和lParm參數是系統傳給鉤子函數的值。
安裝鉤子函數 定義完鉤子函數后,必須將該鉤子安裝到Windows系統中才能生效,安裝鉤子的函數為: HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId) 參數idHook表示待安裝的鉤子函數類型,可以是鍵盤、鼠標或外殼等鉤子,lpfn表示鉤子函數的地址, hMod表示是全局鉤子還是局部鉤子,如果是全局鉤子則鉤子函數必須在DLL文件中,dwThreadId表示鉤子將要起作用的程序ID。
卸載鉤子函數 鉤子函數使用完后必須卸載,這是一個良好 程序員必備的優良品質。卸載鉤子的函數為: BOOL UnhookWindowsHookEx(HHOOK hhk) 參數hhk表示待卸載的鉤子句柄。
詳細代碼 根據上面所述,下面介紹詳細代碼: //定義全局變量 HHOOK hMsgHook; //鉤子句柄 int iClientHeight, iClientWidth; //待畫的客戶區高和寬 Graphics::TBitmap Face; // 從文件調用位圖的控件 HBITMAP hFaceBitmap; //位圖句柄 HWND hClientHandle, hMdiHandle; //MDI主窗口和MDI客戶窗口句柄 LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam ) ; void __fastcall TMainForm::FormPaint(TObject Sender) { iClientHeight = ClientHeight; iClientWidth = ClientWidth; } //設置系統時, 在狀態條上顯示 void __fastcall TMainForm::FormShow(TObject Sender) { //從文件中調入位圖 Face = new Graphics::TBitmap(); Face->LoadFromFile(“d:\\temp\\face.bmp”); hFaceBitmap = Face->Handle; //保存位圖句柄 hClientHandle = ClientHandle; //保存窗口句柄 hMdiHandle = Handle; //保存MDI主窗口句柄 //安裝截取程序消息的鉤子函數 hMsgHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, NULL, GetCurrentThreadId() ); } //鉤子函數,處理系統WM_PAINT和WM_ERASEBKGND消息 LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam ) { LRESULT lReturn=0; MSG cwMessage; cwMessage = (MSG)lParam; if ( cwMessage->hwnd == hClientHandle || cwMessage->hwnd == hMdiHandle) //是發送給子窗口的消息則處理 {if ( cwMessage->message == WM_PAINT || cwMessage->message == WM_ERASEBKGND ) { //重畫用戶窗口 DrawBitmap(hClientHandle, hFaceBitmap, iClientHeight, iClientWidth); } } if ( hMsgHook != NULL) //將消息繼續下傳 lReturn = CallNextHookEx(hMsgHook, nCode, wParam, lParam ); return lReturn; } //卸載鉤子函數 void __fastcall TMainForm::FormClose(TObject Sender, TCloseAction &&Action) { if ( hMsgHook != NULL) UnhookWindowsHookEx( hMsgHook ); if ( Face != NULL ) delete Face; } //在指定的窗口中,畫位圖,填充整個用戶窗口 //Ture為繪制成功,false為繪制失敗 BOOL DrawBitmap(HWND Handle, HBITMAP hBitmap, int iClientHeight, int iClientWidth) { if ( hBitmap == NULL ) return false; BITMAP b; int iBitmapH, iBitmapW; GetObject( hBitmap, sizeof( BITMAP), &&b); iBitmapH = b.bmHeight; iBitmapW = b.bmWidth; int x, y; HDC hClientDC, hMemDC; hClientDC = GetDC(Handle); if ( hClientDC == NULL ) return false; hMemDC = CreateCompatibleDC( hClientDC ); if ( hMemDC == NULL ) { DeleteDC( hClientDC ); return false; } SelectObject( hMemDC, hBitmap ); x = 0; while ( x < iClientWidth ) { y = 0; while ( y < iClientHeight ) {ClientCanvas->Draw(x, y, Face); BitBlt( hClientDC, x, y,iBitmapW, iBitmapH, hMemDC, 0, 0,SRCCOPY ); y = y + iBitmapH; } x = x + iBitmapW; } DeleteDC( hMemDC ); DeleteDC( hClientDC ); return true; }
將上述C++ Builder代碼片段加入用戶的MDI軟件中即可實現任意的MDI程序背景圖
|