考虑到雪花将会很多,并且每个雪花都有自己的行为路径,统一处理比较麻烦,因此自定义一个类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; else rtNewLocation.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);//桌面窗口DC CBitmap 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. 代码下载地址
点击打开链接