亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Visual C++設計超強仿QQ自動伸縮窗口

2019-11-17 04:59:56
字體:
來源:轉載
供稿:網友
  摘要:

  某天在論壇上看到有人發帖詢問QQ自動伸縮窗口是怎么實現的,我也好想知道,于是到百度一搜索,結果不多,來來去去都是那幾篇,下載那些demo運行一下,發覺效果與QQ相差很大,于是決定自己動手做個,要求要近乎完美地模擬這個功能。由于是些效果的東西,貼圖也看不出來,所以文章里就不截圖了,想看效果的就直接運行源代碼的demo吧。

  一、觀察

  模擬前最重要的一步就是觀察,經過半天對QQ的擺弄和摸索,總結出了以下一些特點:

  1、窗口開始粘附時,檢測的是鼠標坐標與桌面邊界的距離,非凡地,粘附在下面的時候,檢測的是與任務欄的距離;

  2、在向上移動窗口時,窗口邊界永遠不會超出桌面上面邊界;

  3、窗口是個 TopMost 風格;

  4、當窗口粘附在上面、左邊或右邊并顯示時,你把鼠標移動到最頂端,光標變成改變窗口大小的圖標,而單單是把窗口的top坐標設置為0是不行的;

  5、粘附在下面的時候,當處于移動狀態,那么窗口的底邊是與任務欄頂邊對齊的,但從隱藏到顯示的時候,窗口的底端是與屏幕底邊對齊的;

  6、隱藏后顯露出來的那條線可能是一個Border,但肯定的是絕不包含Client區域;

  7、關于響應鼠標的進入與移出窗口,絕對不是WM_MOUSEMOVE、WM_MOUSELEAVE。證實:你以及其慢的速度接觸隱藏狀態的QQ邊界,你會發現幾乎是“一觸即發”,你又以及其慢的速度移出顯示狀態的QQ,你會發現它的收縮反而不是“一觸即發”的,而是離邊緣10象素左右。而WM_MOUSEMOVE,WM_MOUSELEAVE,只有在進入、移出Client區域才響應,明顯和QQ不同,其實從第6點也可以知道;

  8、粘附在兩邊的時候,高度會調整為桌面上邊界到任務欄下邊界的距離;

  9、在“拖動時顯示窗口內容”模式下(桌面屬性-外觀-效果),粘附在兩邊的拖動出來時;假如收縮之前高度比收縮后小則回復原來高度,在非“拖動時顯示窗口內容”模式下,光柵會回復原來高度,但釋放左鍵時,高度卻是收縮時調整后的高度,一開始我以為這是個BUG,但我編寫時同樣出現這個問題,發現這兩種模式會影響WM_MOVING參數的意義;

  10、粘附在兩邊的時候當你設置任務欄自動隱藏,QQ窗口會自動調整高度布滿屏幕高度;

  11、窗口顯示或隱藏不是一瞬間的,這點在第9點提到的兩種模式下,會有所不同;

  12、任務欄并不顯示QQ窗口;

  二、編寫代碼

  觀察完畢,就開始編寫了。

  首先新建一個基于對話框的MFC程序,命名為QQHideWnd,在對話框屬性的styles頁把border改為Resizing,你也可同時把Entended styles 的 tool window 鉤上,對于這點我在程序了動態修改了。
在QQHideWndDlg.h頭文件添加以下成員函數:

PRotected:
//修正移動時窗口的大小
void FixMoving(UINT fwSide, LPRECT pRect);
//從收縮狀態顯示窗口
void DoShow();
//從顯示狀態收縮窗口
void DoHide();
//重載函數,只是為了方便調用,實際調用CWnd的SetWindowPos(…)
BOOL SetWindowPos(const CWnd* pWndInsertAfter,LPCRECT pCRect, UINT nFlags = SWP_SHOWWINDOW);

  繼續添加成員變量:

private::BOOL m_isSizeChanged;//窗口大小是否改變了
BOOL m_isSetTimer;//是否設置了檢測鼠標的Timer
INTm_oldWndHeight;//舊的窗口寬度INTm_taskBarHeight;//任務欄高度INTm_edgeHeight;//邊緣高度
INTm_edgeWidth;//邊緣寬度
INTm_hideMode;//隱藏模式
BOOL m_hsFinished;//隱藏或顯示過程是否完成
BOOL m_hiding;//該參數只有在!m_hsFinished才有效
//真:正在隱藏,假:正在顯示

  增加消息響應,需要注重的是有些消息你只有把右下角的 Filter for message設置為window才能看到。

WM_ NCHITTEST
WM_MOVING
WM_CREATE
WM_TIMER

  然后來到對應的cpp文件,在頭部定義一些宏:

//收縮模式#define HM_NONE0//不收縮
#define HM_TOP1//向上收縮
#define HM_BOTTOM2//向下收縮
#define HM_LEFT3//向左收縮
#define HM_RIGHT4//向右收縮
#define CM_ELAPSE200 //檢測鼠標是否離開窗口的時間間隔
#define HS_ELAPSE5//伸縮過程每步的時間間隔
#define HS_STEPS10//伸縮過程分成多少步完成
#define INTERVAL20//觸發粘附時鼠標與屏幕邊界的最小間隔,單位為象素
#define INFALTE10//觸發收縮時鼠標與窗口邊界的最小間隔,單位為象素

  然后在構造函數初始化成員變量:

m_isSizeChanged = FALSE;
m_isSetTimer = FALSE;m_hsFinished = TRUE;
m_hiding = FALSE;m_oldWndHeight = MINCY;
m_taskBarHeight = 30;
m_edgeHeight = 0;
m_edgeWidth=0;
m_hideMode = HM_NONE;

  完成了一些初始的工作,那么就開始進入要害的函數實現了。首先是在OnCreate做些窗口的初始化和獲得一些系統信息。

  【代碼一】

int CQQHideWndDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1; // TODO: Add your specialized creation code here//獲得任務欄高度
CWnd* p;
p = this->FindWindow("Shell_TrayWnd",NULL);
if(p != NULL)
{
CRect tRect;
p->GetWindowRect(tRect);
m_taskBarHeight = tRect.Height();
}//修改風格使得他不在任務欄顯示
ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW);
//去掉關閉按鍵(假如想畫3個按鍵的話)
//ModifyStyle(WS_SYSMENU,NULL);//獲得邊緣高度和寬度
m_edgeHeight = GetSystemMetrics(SM_CYEDGE);
m_edgeWidth = GetSystemMetrics(SM_CXFRAME);return 0;
}

  接著如何知道鼠標進入或移出窗口呢?在前面我已經證實了WM_MOUSEMOVE和WM_MOUSELEAVE不符合我們的要求,于是我用了WM_ NCHITTEST這個消息,你可以看到我在這個消息響應函數中用了兩個SetTimer,一個用于檢測鼠標是否離開,一個用于伸縮過程,不管你喜歡不喜歡,要達到第7點和第11點,這個是必須的,考慮的效率問題,在不需要的時候關閉這些Timer就好了。

  【代碼二】

UINT CQQHideWndDlg::OnNcHitTest(CPoint point)
{
// TODO: Add your message handler code here and/or call default
CString str;
str.Format("Mouse (%d,%d)",point.x,point.y);
GetDlgItem(IDC_CURSOR)->SetWindowText(str);
if(m_hideMode != HM_NONE && !m_isSetTimer &&
//防止鼠標超出屏幕右邊時向右邊收縮造成閃爍
point.x < GetSystemMetrics(SM_CXSCREEN) + INFALTE)
{ //鼠標進入時,假如是從收縮狀態到顯示狀態則開啟Timer
SetTimer(1,CM_ELAPSE,NULL);
m_isSetTimer = TRUE; m_hsFinished = FALSE;
m_hiding = FALSE;
SetTimer(2,HS_ELAPSE,NULL); //開啟顯示過程
}
return CDialog::OnNcHitTest(point);
}

  然后在OnTimer中

  【代碼三】

void CQQHideWndDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if(nIDEvent == 1 )
{
POINT curPos;
GetCursorPos(&curPos); CString str;
str.Format("Timer On(%d,%d)",curPos.x,curPos.y);
GetDlgItem(IDC_TIMER)->SetWindowText(str);CRect tRect;
//獲取此時窗口大小
GetWindowRect(tRect);
//膨脹tRect,以達到鼠標離開窗口邊沿一定距離才觸發事件
tRect.InflateRect(INFALTE,INFALTE); if(!tRect.PtInRect(curPos)) //假如鼠標離開了這個區域
{
KillTimer(1); //關閉檢測鼠標Timer
m_isSetTimer = FALSE;
GetDlgItem(IDC_TIMER)->SetWindowText("Timer Off");m_hsFinished = FALSE;
m_hiding = TRUE;
SetTimer(2,HS_ELAPSE,NULL); //開啟收縮過程
}
}if(nIDEvent == 2)
{
if(m_hsFinished) //假如收縮或顯示過程完畢則關閉Timer
KillTimer(2);
else
m_hiding ? DoHide() : DoShow();
}
CDialog::OnTimer(nIDEvent);
}

  暫時不管OnTimer中的DoHide(); DoShow();

  先來看看核心的函數之一的 FixMoving,該函數在OnMoving中被調用,FixMoving通過檢測鼠標位置和窗口位置來決定窗口的收縮模式,并修正粘附邊界時窗口的位置,從而達到像移動QQ時出現的效果。

  【代碼四】

void CQQHideWndDlg::FixMoving(UINT fwSide, LPRECT pRect)
{
POINT curPos;
GetCursorPos(&curPos);
INT screenHeight = GetSystemMetrics(SM_CYSCREEN);
INT screenWidth = GetSystemMetrics(SM_CXSCREEN);
INT height = pRect->bottom - pRect->top;
INT width = pRect->right - pRect->left;if (curPos.y <= INTERVAL)
{ //粘附在上邊
pRect->bottom = height - m_edgeHeight;
pRect->top = -m_edgeHeight;
m_hideMode = HM_TOP;
}
else if(curPos.y >= (screenHeight - INTERVAL - m_taskBarHeight))
{ //粘附在下邊
pRect->top = screenHeight - m_taskBarHeight - height;
pRect->bottom = screenHeight - m_taskBarHeight;
m_hideMode = HM_BOTTOM;
}
else if (curPos.x < INTERVAL)
{ //粘附在左邊
if(!m_isSizeChanged)
{
CRect tRect;
GetWindowRect(tRect);
m_oldWndHeight = tRect.Height();
}
pRect->right = width;
pRect->left = 0;
pRect->top = -m_edgeHeight;
pRect->bottom = screenHeight - m_taskBarHeight;
m_isSizeChanged = TRUE;
m_hideMode = HM_LEFT;
}
else if(curPos.x >= (screenWidth - INTERVAL))
{ //粘附在右邊
if(!m_isSizeChanged)
{
CRect tRect;
GetWindowRect(tRect);
m_oldWndHeight = tRect.Height();
}
pRect->left = screenWidth - width;
pRect->right = screenWidth;
pRect->top = -m_edgeHeight;
pRect->bottom = screenHeight - m_taskBarHeight;
m_isSizeChanged = TRUE;
m_hideMode = HM_RIGHT;
}
else
{ //不粘附
if(m_isSizeChanged)
{ //假如收縮到兩邊,則拖出來后會變回原來大小
//在"拖動不顯示窗口內容下"只有光柵變回原來大小
pRect->bottom = pRect->top + m_oldWndHeight;
m_isSizeChanged = FALSE;
}
if(m_isSetTimer)
{ //假如Timer開啟了,則關閉之
if(KillTimer(1) == 1)
m_isSetTimer = FALSE;
}
m_hideMode = HM_NONE;
GetDlgItem(IDC_TIMER)->SetWindowText("Timer off");
}
}

  收縮模式和位置決定后,剩下的工作就由最后兩個核心函數完成了:實現收縮的DoHide(),實現伸展的DoShow()。在這兩個過程中m_hsFinished,m_hiding 這兩個變量起到很重要的控制作用。由于伸縮過程沒完成時,hsFinished始終為FALSE,所以Timer 2 不會關閉,于是在OnTimer中會重復調用這兩個函數之一,在這兩個函數體內,窗口位置有規律地遞減或遞增就可以達到QQ的“抽屜”效果了,有趣的是即使伸縮過程還沒完成,你也可以在這個過程中改變m_hiding這個值來決定他是伸還是縮,正如QQ一樣。你可以把Timer 2 的事件間隔調大一點,然后在窗口伸縮時,鼠標往返地進出窗口就會很輕易看到這樣有趣的效果(還沒縮進去又被拉了出來,或者還沒拉出來又縮進去了)。

  【代碼五】

void CQQHideWndDlg::DoHide()
{
if(m_hideMode == HM_NONE)
return;CRect tRect;
GetWindowRect(tRect);INT height = tRect.Height();
INT width = tRect.Width();INT steps = 0;switch(m_hideMode)
{
case HM_TOP:
steps = height/HS_STEPS;
tRect.bottom -= steps;
if(tRect.bottom <= m_edgeWidth)
{ //你可以把下面一句替換上面的 ...+=-=steps 達到取消抽屜效果
//更好的辦法是添加個BOOL值來控制,其他case同樣.
tRect.bottom = m_edgeWidth;
m_hsFinished = TRUE; //完成隱藏過程
}
tRect.top = tRect.bottom - height;
break;
case HM_BOTTOM:
steps = height/HS_STEPS;
tRect.top += steps;
if(tRect.top >= (GetSystemMetrics(SM_CYSCREEN) - m_edgeWidth))
{
tRect.top = GetSystemMetrics(SM_CYSCREEN) - m_edgeWidth;
m_hsFinished = TRUE;
}
tRect.bottom = tRect.top + height;
break;
case HM_LEFT:
steps = width/HS_STEPS;
tRect.right -= steps;
if(tRect.right <= m_edgeWidth)
{
tRect.right = m_edgeWidth;
m_hsFinished = TRUE;
}
tRect.left = tRect.right - width;
tRect.top = -m_edgeHeight;
tRect.bottom = GetSystemMetrics(SM_CYSCREEN) - m_taskBarHeight;
break;
case HM_RIGHT:
steps = width/HS_STEPS;
tRect.left += steps;
if(tRect.left >= (GetSystemMetrics(SM_CXSCREEN) - m_edgeWidth))
{
tRect.left = GetSystemMetrics(SM_CXSCREEN) - m_edgeWidth;
m_hsFinished = TRUE;
}
tRect.right = tRect.left + width;
tRect.top = -m_edgeHeight;
tRect.bottom = GetSystemMetrics(SM_CYSCREEN) - m_taskBarHeight;
break;
default:
break;
}SetWindowPos(&wndTopMost,tRect);
}

  【代碼六】

void CQQHideWndDlg::DoShow()
{
if(m_hideMode == HM_NONE)
return;CRect tRect;
GetWindowRect(tRect);
INT height = tRect.Height();
INT width = tRect.Width();INT steps = 0;switch(m_hideMode)
{
case HM_TOP:
steps = height/HS_STEPS;
tRect.top += steps;
if(tRect.top >= -m_edgeHeight)
{ //你可以把下面一句替換上面的 ...+=-=steps 達到取消抽屜效果
//更好的辦法是添加個BOOL值來控制,其他case同樣.
tRect.top = -m_edgeHeight;
m_hsFinished = TRUE; //完成顯示過程
}
tRect.bottom = tRect.top + height;
break;
case HM_BOTTOM:
steps = height/HS_STEPS;
tRect.top -= steps;
if(tRect.top <= (GetSystemMetrics(SM_CYSCREEN) - height))
{
tRect.top = GetSystemMetrics(SM_CYSCREEN) - height;
m_hsFinished = TRUE;
}
tRect.bottom = tRect.top + height;
break;
case HM_LEFT:
steps = width/HS_STEPS;
tRect.right += steps;
if(tRect.right >= width)
{
tRect.right = width;
m_hsFinished = TRUE;
}
tRect.left = tRect.right - width;
tRect.top = -m_edgeHeight;
tRect.bottom = GetSystemMetrics(SM_CYSCREEN) - m_taskBarHeight;
break;
case HM_RIGHT:
steps = width/HS_STEPS;
tRect.left -= steps;
if(tRect.left <= (GetSystemMetrics(SM_CXSCREEN) - width))
{
tRect.left = GetSystemMetrics(SM_CXSCREEN) - width;
m_hsFinished = TRUE;
}
tRect.right = tRect.left + width;
tRect.top = -m_edgeHeight;
tRect.bottom = GetSystemMetrics(SM_CYSCREEN) - m_taskBarHeight;
break;
default:
break;
}SetWindowPos(&wndTopMost,tRect);
}BOOL CQQHideWndDlg::SetWindowPos(const CWnd* pWndInsertAfter, LPCRECT pCRect, UINT nFlags)
{
return CDialog::SetWindowPos(pWndInsertAfter,pCRect->left, pCRect->top,
pCRect->right - pCRect->left, pCRect->bottom - pCRect->top, nFlags);
}

  到此,程序終于完成了。在我的源代碼中還有對WM_SIZING的處理和定義了與之相關的宏,這些主要是控制窗口在調整大小時不能超過最小的寬度和高度,與QQ的自動伸縮無關,所以不在這里提及了。

  三、結束語

  雖然還不能算是完美的模擬,但效果已經非常非常的接近了。也許有人會希奇為什么要用Tool Window 風格,這是因為,這樣在任務欄中不會顯示窗口。從QQ的標題欄高度也可以判定出他也是這種風格,但這樣一來就不能擁有最小化、最大化按鍵了。實際上QQ的最大化、最小化和關閉按鍵都是用DC畫上去的。如何在Caption上增加按鍵,外國一些開源網站有源代碼,我下載并看了一下,發現里面有個知識點很有趣,那就是更改消息路由,有愛好的可以去下載來學習一下。

  QQ的成功很大部分在于他的界面比較人性化(用了MSN后深有感受),而這些界面實現起來原理也許很簡單,難的是觀察東西心要細、設計東西心要密、開發東西心要異。

  查閱關于VC的全部文檔


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久久久久999| 亚洲欧洲国产精品| 91在线精品视频| 欧美视频在线看| 国产一区视频在线播放| 欧美电影在线观看网站| 国产美女精彩久久| 国产一区二区三区直播精品电影| 久久久亚洲天堂| 欧美在线日韩在线| 色午夜这里只有精品| 国产亚洲精品激情久久| 欧美性xxxxx| 欧美激情免费看| 91福利视频在线观看| 5278欧美一区二区三区| 国产日韩精品在线观看| 在线日韩av观看| 亚洲国产精品女人久久久| 91精品免费久久久久久久久| 国产精品黄页免费高清在线观看| 国a精品视频大全| 久久久亚洲成人| 国产精品久久久久久久久久99| 97超碰国产精品女人人人爽| 久久综合色88| 国内精品久久久| 久久亚洲精品一区二区| 精品亚洲永久免费精品| 久久久国产精品x99av| 欧美激情网站在线观看| 亚洲欧美精品中文字幕在线| 国产一区二区三区在线观看视频| 欧美超级免费视 在线| 91高清视频免费观看| 91久久久久久| 国产va免费精品高清在线| 亚洲精品资源在线| 国产精品av免费在线观看| 亚洲奶大毛多的老太婆| 国产精品视频免费在线观看| 国产色视频一区| 欧美成人免费全部| 91sa在线看| 日韩在线不卡视频| 伦理中文字幕亚洲| 性欧美亚洲xxxx乳在线观看| 日本不卡高字幕在线2019| 亚洲男人天堂网站| 91九色在线视频| 国外成人在线播放| 亚洲欧美另类国产| 国产视频观看一区| 国产精品狠色婷| 久久久免费观看视频| 欧美激情免费看| 91chinesevideo永久地址| 精品国内亚洲在观看18黄| 国产精欧美一区二区三区| 少妇av一区二区三区| 欧美日韩国产中文字幕| 日韩中文在线中文网三级| 日韩综合视频在线观看| 另类图片亚洲另类| 精品偷拍一区二区三区在线看| 69av在线播放| 超碰日本道色综合久久综合| 狠狠色狠狠色综合日日五| 亚洲免费小视频| 奇米四色中文综合久久| 国内精品久久久久久久| 2019日本中文字幕| 国产精品久久久久久久av大片| 亚洲免费小视频| 日韩中文字幕不卡视频| 中文字幕亚洲欧美在线| 亚洲深夜福利在线| 亚洲r级在线观看| 亚洲精品中文字幕女同| 欧美成年人视频| 欧美裸体xxxx极品少妇| 精品欧美aⅴ在线网站| 久久久av免费| 亚洲日本中文字幕免费在线不卡| 91精品国产91久久久久久最新| 亚洲人成电影网| 国产亚洲a∨片在线观看| 综合网日日天干夜夜久久| 亚洲区中文字幕| 成人激情视频小说免费下载| 97精品免费视频| 夜夜嗨av一区二区三区免费区| 欧美性猛交xxxx免费看久久久| 国产亚洲精品美女| 国产亚洲精品成人av久久ww| 一本色道久久88综合日韩精品| 91免费精品国偷自产在线| 国产精品大片wwwwww| 一级做a爰片久久毛片美女图片| 日韩av成人在线观看| 一区二区亚洲欧洲国产日韩| 精品国产一区二区三区四区在线观看| 4388成人网| 日韩国产欧美精品一区二区三区| 亚洲第一页自拍| 麻豆乱码国产一区二区三区| 日本精品免费一区二区三区| 亚洲国产精品久久久久秋霞蜜臀| 97久久超碰福利国产精品…| 91产国在线观看动作片喷水| 成人黄色大片在线免费观看| 欧美久久精品午夜青青大伊人| 国产精品aaa| 国产精品网红福利| 91免费综合在线| 欧美日韩国产色| 亚洲精品午夜精品| 成人性生交大片免费看小说| 亚洲美女免费精品视频在线观看| 国模视频一区二区三区| 亚洲男人天堂网站| 亚洲缚视频在线观看| 亚洲精品98久久久久久中文字幕| 中文字幕在线看视频国产欧美在线看完整| 日韩视频免费中文字幕| 精品性高朝久久久久久久| 欧美美最猛性xxxxxx| 国产一区二区三区四区福利| 国产精品久久久久久av| 国产成人精品一区二区三区| 一本一道久久a久久精品逆3p| 日韩中文娱乐网| 91色在线视频| 亚洲成人中文字幕| 欧美日韩免费在线| 91精品国产成人www| 国产一区二区三区日韩欧美| 久久久久久香蕉网| 国产va免费精品高清在线| 亚洲精品网站在线播放gif| 91精品国产亚洲| 国产成人精品视| 国产日韩欧美电影在线观看| 日韩电影大片中文字幕| 2019中文字幕在线免费观看| 亚洲91av视频| 久久久久久久久久久国产| 日韩女在线观看| 精品国产区一区二区三区在线观看| 国产91色在线免费| 国产欧美一区二区三区久久人妖| 欧美疯狂做受xxxx高潮| 法国裸体一区二区| 国产精品一区二区性色av| 久久香蕉国产线看观看网| 亚洲天堂av女优| 精品综合久久久久久97| 精品中文字幕在线| 2021久久精品国产99国产精品| 久久久91精品国产| 91网站在线免费观看| 裸体女人亚洲精品一区| 91精品国产一区| 国产成人精品久久久|