2007新的一年即將來臨,新版本的QQ估計也要跟我們相見。在此獻上本人寫於8月份的一個練習程序。主要是希望騰訊做界面的同志能否把創建異形窗體函數 SetWindowRgn 放到合適的位置,別讓拖動窗體改變大小時出現用做 MASK 的紫色區域;再者與大家分享不指定窗體風格 WS_THICKFRAME(對於對話框,相當指定其屬性 Border 為 Resizing ),用代碼實現窗體拖放,任意改變其尺寸。
一、SetWindowRgn的合適位置
1、在void C**Dlg::OnPaint()裡調用SetWindowRgn,可以在內存畫圖完畢准備顯示到屏幕前調用,如下:
void C**Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this);
...
}
else
{
CPaintDC dc(this); // 用於繪制的設備上下文
CRect rcClient;
GetClientRect(&rcClient);
//構造內存DC,用於畫圖
CDC m_MemDC;
m_MemDC.CreateCompatibleDC(&dc);
CBitmap btScreen;
btScreen.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
m_MemDC.SelectObject(&btScreen);
btScreen.DeleteObject();
//這裡畫圖
...
//創建不規則窗體
ChangeWindowRgn(&m_MemDC);//這裡面調用了SetWindowRgn
//畫到顯示器上
dc.BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), &m_MemDC, 0, 0, SRCCOPY);
m_MemDC.DeleteDC();
}
}
void C**Dlg::ChangeWindowRgn(CDC *pDC)
{
COLORREF col = RGB(255,0,255);
CRect rcClient;
GetClientRect (rcClient);
CRgn rgn;
rgn.CreateRectRgn (0, 0, rcClient.Width(), rcClient.Height());
...
SetWindowRgn (rgn, TRUE);
}
2、在void C**Dlg::OnShowWindow()裡調用SetWindowRgn, 如下:
void C**Dlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
CWnd::OnShowWindow(bShow, nStatus);
// TODO: 在此處添加消息處理程序代碼
if(bShow)
{
CRect rc;
this->GetClientRect(&rc);
CRgn rgnMain;
rgnMain.CreateRoundRectRgn(0, 0, rcClient.Width(), rcClient.Height());
...
SetWindowRgn( rgnMain, TRUE );
}
}
二、手動做“Resizing對話框”
該思路啟發於徐景周的精靈特效窗體。要想點擊窗體客戶區不放能移動窗體,傳統的做法是模擬消息點擊標題。
void C**Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息處理程序代碼
PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,0);
CDialog::OnLButtonDown(nFlags, point);
}
這樣很方便實現效果。但不足是窗體被移到屏幕上方,大部分在屏幕所能顯示以外以後放開鼠標,窗體會自動向下對齊。徐景周的精靈特效窗體用了SetTimer和MoveWindow結合使用,這樣窗體想被移到哪裡都可以。正因為如此,讓我想到拖放窗體的好思路。當然我們完全可以利用窗體風格WS_THICKFRAME,讓系統來為我們做事。
但是如果我們要指定窗體某個部位可以拖放窗體時,像QQ切換主題後,拖放很不方便。可以拖放的區域不是最左,最右,最上,最下,沒有別的地方可以點擊拖放窗體了。
如何實現,簡單說就是在鼠標按下時判斷是否點在規定區域內,是的話啟動記時器。然後在記時器裡面定時器裡面對光標判斷當前位置與之前位置,從而調用MoveWindow讓窗體朝響應方向拉伸或收縮。代碼較瑣碎,請見例子。
void C**Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CRect rc(*,*,*,*);
if(rc.PtInRect(point))
{
SetTimer(1,20,NULL);//啟動記時器
return;
}
CDialog::OnLButtonDown(nFlags, point);
}
void C**Dlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
switch(nIDEvent)
{
case(1):
{
CRect rcW;
POINT point;
GetWindowRect(rcW);//
//實現拖動時窗體跟著右下角拉伸
::GetCursorPos(&point); //得到“當前位置”
if(point.y<rcW.bottom-400)
{
MoveWindow(m_rcCurRect.left ,point.y-m_ptCurPoint.y, rcW.Width(),
m_rcCurRect.bottom-(point.y-m_ptCurPoint.y), true);
CRect rc;
GetWindowRect(rc);
m_rcCurRect = rc;//保存“之前位置”
}
Invalidate();
}
break;
...
Default:
break;
}
Dialog::OnTimer(nIDEvent);
}
代碼在Visual2005下編譯,在WindowXP運行通過。預覽圖如下:
預覽圖