一.
综述
考虑到雪花将会很多,并且每个雪花都有自己的行为路径,统一处理比较麻烦,因此自定义一个类CSnowflake,它所呈现的主要接口有两个:下落和“死亡”判断。下落路径由雪花对象自身处理,主框架中只是采用定时器来控制其下落。当然,雪花落到屏幕底后就相当于“死亡”了,为了保持活动雪花总数大致不变,我又开启了一个定时器,用来产生雪花。在用户交互上我做了一个托盘,可以显示提示,右键弹出菜单。还有一个小问题——程序运行之后即隐藏界面,自己试了许多方法,也在网上差了许多,最后还是在消息WM_WINDOWPOSCHANGING响应中添加lpwndpos->flags&=SWP_HIDEWINDOW并且去掉MFC生成的代码这个方法来的彻底。
二. 程序显示
1. 雪花
2. 托盘
主要描述其下落方法。
说明:
其中的hwndDesktop是在构造函数中使用以下代码获得的
其中的rtLocation指的是雪花当前矩形位置,rtDesktop指的是绘制屏幕矩形范围。
位图我画了四个,随机选择一个。
四. 主对话框中的处理
1. 定时器处理
托盘的添加是在OnInitDialog中的:
对其图标的消息处理函数为:
1. Bug
点击托盘菜单时,雪花会停止下落。
2. 说明
本程序只适于静态桌面环境下。
3. 奋斗无止境
4. 代码下载地址
点击打开链接
考虑到雪花将会很多,并且每个雪花都有自己的行为路径,统一处理比较麻烦,因此自定义一个类CSnowflake,它所呈现的主要接口有两个:下落和“死亡”判断。下落路径由雪花对象自身处理,主框架中只是采用定时器来控制其下落。当然,雪花落到屏幕底后就相当于“死亡”了,为了保持活动雪花总数大致不变,我又开启了一个定时器,用来产生雪花。在用户交互上我做了一个托盘,可以显示提示,右键弹出菜单。还有一个小问题——程序运行之后即隐藏界面,自己试了许多方法,也在网上差了许多,最后还是在消息WM_WINDOWPOSCHANGING响应中添加lpwndpos->flags&=SWP_HIDEWINDOW并且去掉MFC生成的代码这个方法来的彻底。
二. 程序显示
1. 雪花
2. 托盘
主要描述其下落方法。
BOOL CSnowflake::Down()
{if (bDie)return FALSE;CRect rtNewLocation;srand((UINT)time(NULL));//随机种子if (rand()%2)rtNewLocation.left=rtLocation.left+rand()%10;elsertNewLocation.left=rtLocation.left-rand()%5;rtNewLocation.right=rtNewLocation.left+rtLocation.Width();rtNewLocation.top=rtLocation.top+iSpeed;rtNewLocation.bottom=rtNewLocation.top+rtLocation.Height();if (rtNewLocation.bottom>=rtDesktop.bottom)//超出绘制屏幕{bDie=TRUE;//设置死亡标志return FALSE;}else//下落{//擦除原雪花RedrawWindow(hwndDesktop,&rtLocation,NULL,RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);HDC hDesktopDC=GetDC(hwndDesktop);CDC desktopDC;desktopDC.Attach(hDesktopDC);//桌面窗口DCCBitmap bmp;switch(bmpID){case 0:bmp.LoadBitmap(IDB_BITMAP1);break;case 1:bmp.LoadBitmap(IDB_BITMAP2);break;case 2:bmp.LoadBitmap(IDB_BITMAP3);break;case 3:bmp.LoadBitmap(IDB_BITMAP4);break;default:break;}//重绘原矩形区域CDC bmpDC;bmpDC.CreateCompatibleDC(&desktopDC);CBitmap *poldbmp=bmpDC.SelectObject(&bmp);desktopDC.TransparentBlt(rtNewLocation.left,rtNewLocation.top,rtNewLocation.Width(),rtNewLocation.Height(),&bmpDC,0,0,rtNewLocation.Width(),rtNewLocation.Height(),RGB(0,0,0));//将底色白色设为透明bmpDC.SelectObject(poldbmp);desktopDC.Detach();ReleaseDC(hwndDesktop,hDesktopDC);rtLocation=rtNewLocation;//赋新位置return TRUE;}
}
说明:
其中的hwndDesktop是在构造函数中使用以下代码获得的
HWND hProgMan=::FindWindowW(L"ProgMan",NULL);
if(hProgMan)
{HWND hShellDefView=::FindWindowEx(hProgMan,NULL,L"SHELLDLL_DefView",NULL);if(hShellDefView)hwndDesktop=::FindWindowEx(hShellDefView,NULL,L"SysListView32",L"FolderView");
}
if (hwndDesktop==NULL)bDie=TRUE;
其中的rtLocation指的是雪花当前矩形位置,rtDesktop指的是绘制屏幕矩形范围。
位图我画了四个,随机选择一个。
四. 主对话框中的处理
1. 定时器处理
void CSnow2Dlg::OnTimer(UINT_PTR nIDEvent)
{switch(nIDEvent){case 1://控制雪花下落{if(WAIT_TIMEOUT==WaitForSingleObject(m_handleEvent,100))break;ResetEvent(m_handleEvent);std::vector<CSnowflake> tempflakes;for (std::vector<CSnowflake>::iterator iter=snowflakes.begin();iter!=snowflakes.end();++iter){if (iter->IsDie()==FALSE){iter->Down();tempflakes.push_back(*iter);}}snowflakes.clear();snowflakes=tempflakes;SetEvent(m_handleEvent);}break;case 2://判断雪花死亡状态,产生新雪花{if(WAIT_TIMEOUT==WaitForSingleObject(m_handleEvent,100))break;ResetEvent(m_handleEvent);if (snowflakes.size()<MAX_COUNT_FLAKES){srand(static_cast<UINT>(time(NULL)));static int count=1;for (int i=0;i!=count;++i){CSnowflake flake(rand()%MAX_BMP_COUNT,15,15,rand()%m_iDesktopWidth+1,rand()%5+2,CRect(0,0,m_iDesktopWidth,m_iDesktopHeight));snowflakes.push_back(flake);}++count;if (count>10)count=10;}SetEvent(m_handleEvent);}break;default:break;}CDialog::OnTimer(nIDEvent);
}
说明:此处有一std::vector<CSnowflake>类型的snowflakes成员变量,这个保存了当前所有活动雪花,若雪花已“死”,将会被移除出此向量,这样“死亡”的雪花就可在屏幕任务栏积累。然而在两个定时器中都会访问这个向量,于是为了防止访问冲突,设置了一个同步事件m_handleEvent。
托盘的添加是在OnInitDialog中的:
m_nid.cbSize=sizeof(NOTIFYICONDATA);
m_nid.hWnd=this->m_hWnd;
m_nid.uID=IDR_MAINFRAME;
m_nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP|NIF_INFO;
m_nid.uCallbackMessage=UM_TRAY;//自定义的消息名称
m_nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
wcscpy_s(m_nid.szTip,L"桌面下雪程序");//信息提示条
wcscpy_s(m_nid.szInfo,L"哦,下雪了");//信息提示条
wcscpy_s(m_nid.szInfoTitle,L"桌面下雪程序提示");//信息提示条
m_nid.dwInfoFlags=NIIF_INFO;
Shell_NotifyIcon(NIM_ADD,&m_nid);//在托盘区添加图标
对其图标的消息处理函数为:
LRESULT CSnow2Dlg::OnTray(WPARAM wParam,LPARAM lParam)
{if(wParam!=IDR_MAINFRAME) return 1;switch(lParam) {case WM_RBUTTONDOWN:{CPoint pos;GetCursorPos(&pos);//得到鼠标位置 CMenu menu;menu.LoadMenuW(IDR_TRAYMENU);CMenu *psubmenu=menu.GetSubMenu(0);SetForegroundWindow(); //使在菜单外点击时菜单消失psubmenu->TrackPopupMenu(TPM_LEFTALIGN,pos.x,pos.y,this);//确定弹出式菜单的位置} break;default:break;} return 0;
}
此处有一个右键弹出菜单。菜单的命令响应就不列出了。
1. Bug
点击托盘菜单时,雪花会停止下落。
2. 说明
本程序只适于静态桌面环境下。
3. 奋斗无止境
4. 代码下载地址
点击打开链接