桌面飘雪!第二部分






4.59/5 (9投票s)
本文档解释了如何创建一个在桌面飘雪的应用程序。

引言
自从我发表上一篇文章以来,我收到了许多用户关于如何改进程序的询问。本文档演示了如何通过直接在桌面上下文中使用的绘图函数,创建飘落在桌面上的雪花。当应用程序启动时,它会创建一个雪花数组并启动每个雪花的计时器。对 RedrawWindow
函数的操纵允许在桌面图标的后面和上面绘制雪花,使应用程序更具视觉冲击力。
通用步骤
为了更好地随机化,起始坐标、计时器间隔、雪花位置(在桌面图标之上或之后)和大小应预先定义,如下所示
srand((unsigned)time(NULL));
for(int i=0; i<nArray; i++)
{
// the snow flake movement timer
nTimer = rand()*70/RAND_MAX+10;
arTimers.Add(nTimer);
// snow flake start position
nPosX = rand()*rcWorkArea.Width()/RAND_MAX;
arPositions.Add(nPosX);
// draw the flake over or behind the icons
nOver = rand()*100/RAND_MAX;
if(nOver>50)
arOverIcons.Add(1);
else
arOverIcons.Add(0);
// draw a big or small flake
nBig = rand()*100/RAND_MAX;
if(nBig<50)
arBigFlakes.Add(1);
else
arBigFlakes.Add(0);
}
然后,创建雪花并启动计时器
...
CFlake* pFlake = new CFlake(nPosX, nBig, nOver);
m_arFlakes.Add(pFlake);
SetTimer(i+1,nTimer,0);
...
当计时器事件发生时,在桌面上移动相应的雪花
void CMainFrame::OnTimer(UINT nIDEvent)
{
if(nIDEvent >= 0 && nIDEvent <= (UINT)m_arFlakes.GetSize())
{
CFlake* pFlake = m_arFlakes.GetAt(nIDEvent-1);
if(pFlake)
pFlake->Move();
}
}
如上所示,类 CFlake
(C# 代码中的 Flake
)负责桌面绘图。
不幸的是,Windows Vista 不允许正确地使用 RDW_NOERASE
标志的 RedrawWindow
函数来处理桌面窗口。因此,在这种情况下,您无法在图标后面绘制雪花。
大部分的降雪算法都来自我上一篇文章,除了绘图函数之外。
HDC hDC = GetDC(m_hWndDesktop);
if(hDC)
{
RECT rc;
rc.left = m_nCurrentX;
rc.top = m_nCurrentY;
rc.right = m_nCurrentX+15;
rc.bottom = m_nCurrentY+15;
// 15 is for a little drift at the bottom of the desktop
int nTestHeight = m_nScreenHeight - 15;
// redraw the desktop window right away
if(m_nCurrentY<nTestHeight) // snow drift here
RedrawWindow(m_hWndDesktop, &rc, NULL, RDW_INVALIDATE
| RDW_ERASE | RDW_UPDATENOW );
m_nCurrentY += 3;
m_nCounter++;
if(m_nCounter == 15)
{
if((rand()*10/RAND_MAX)>5) m_nIncrement = 1;
else m_nIncrement = -1;
m_nCounter = 0;
}
m_nCurrentX += m_nIncrement;
if(m_nCurrentY>m_nScreenHeight)
{
m_nCurrentY = 0;
m_nCurrentX = abs(rand()*m_nScreenWidth/RAND_MAX);
if(abs(rand()*100/RAND_MAX)>50)
m_bIsBigFlake = TRUE;
else
m_bIsBigFlake = FALSE;
}
// Store DC settings
int storedDC = SaveDC(hDC);
rc.left = m_nCurrentX;
rc.top = m_nCurrentY;
rc.right = m_nCurrentX+15;
rc.bottom = m_nCurrentY+15;
HPEN pOldPen = (HPEN)SelectObject(hDC, m_hFlakePen);
if(m_bIsBigFlake) // draw big flake here
{
MoveToEx(hDC, m_nCurrentX+7, m_nCurrentY, 0);
LineTo(hDC, m_nCurrentX+7, m_nCurrentY+15);
...
}
else // draw small flake here
{
...
}
// specify RDW_NOERASE to keep the desktop from drawing the background
if(!m_bIsVista)
{
if(!m_bOverIcons && m_nCurrentY<m_nScreenHeight)
RedrawWindow(m_hWndDesktop, &rc, NULL, RDW_INVALIDATE
| RDW_NOERASE | RDW_UPDATENOW);
}
SelectObject(hDC, pOldPen);
// Restore DC settings to their original values
RestoreDC(hDC, storedDC);
// Release the DC
ReleaseDC(m_hWndDesktop, hDC);
}
最后,当应用程序关闭时,在 CFlake
析构函数(当前屏幕上的雪花位置)和 CMainFrame::OnClose
函数(桌面底部的雪堆)中清理桌面。
CFlake::~CFlake()
{
...
RedrawWindow(m_hWndDesktop, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
}
void CMainFrame::OnClose()
{
...
// redraw the snow drifts area
if(hWndDesktop)
{
CRect rcWorkArea;
SystemParametersInfo(SPI_GETWORKAREA,0,(LPVOID)&rcWorkArea,0);
rcWorkArea.top = rcWorkArea.bottom - 16;
::RedrawWindow(hWndDesktop, &rcWorkArea, NULL, RDW_INVALIDATE | RDW_ERASE |
RDW_UPDATENOW );
}
}
已知问题
- 当启用活动桌面时,应用程序无法正常工作。
- 使用此代码在 Windows Vista 下在图标后绘制雪花尚未实现。
欢迎对这段代码提出任何改进、评论或建议。