|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionSince my previous article was published, I received many user inquiries about how to improve the program. This article demonstrates how to create snow flakes that fall on your desktop, by using the draw functions directly in the desktop context. When the application starts, it creates an array of snow flakes and starts the timers for each flake. Manipulation of the General StepsFor better randomizing, the start coordinate, timer interval, flake position (over or behind the desktop icons) and size should be pre-defined as shown below: 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);
}
Then, create the flake and start the timer: ...
CFlake* pFlake = new CFlake(nPosX, nBig, nOver);
m_arFlakes.Add(pFlake);
SetTimer(i+1,nTimer,0);
...
When the timer event occurs, move the appropriate flake on the desktop: void CMainFrame::OnTimer(UINT nIDEvent)
{
if(nIDEvent >= 0 && nIDEvent <= (UINT)m_arFlakes.GetSize())
{
CFlake* pFlake = m_arFlakes.GetAt(nIDEvent-1);
if(pFlake)
pFlake->Move();
}
}
As shown above, class Most of the snow fall algorithm was taken from my previous article, except the drawing functions: 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);
}
Finally, when the application closes, clean up the desktop in the 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 );
}
}
Known Problems
Any improvements, comments or suggestions to this code are welcome.
|
||||||||||||||||||||||