Click here to Skip to main content
15,881,559 members
Articles / Programming Languages / C#
Article

Falling Snow on Your Desktop! Part II

Rate me:
Please Sign up or sign in to vote.
4.59/5 (9 votes)
16 Dec 2007CPOL1 min read 212.6K   3.9K   55   14
This article explains how to create an application that makes it snow on the desktop.
Screenshot - FallingSnow

Introduction

Since 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 RedrawWindow function allows the drawing of flakes behind and over the desktop icons, making the application more impressive.

General Steps

For better randomizing, the start coordinate, timer interval, flake position (over or behind the desktop icons) and size should be pre-defined as shown below:

C++
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:

C++
...
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:

C++
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 CFlake (Flake in C# code) is responsible for the desktop drawing.
Unfortunately, Windows Vista does not allow the use of the RedrawWindow function with the RDW_NOERASE flag for the desktop Window correctly. So, you cannot draw the flakes behind the icons in this case.

Most of the snow fall algorithm was taken from my previous article, except the drawing functions:

C++
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 destructor (the current flake on screen position), and in the CMainFrame::OnClose function (snow drift at the bottom of the desktop):

C++
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

  • Application does not work properly when the Active Desktop is enabled.
  • Drawing the snowflakes behind the icons with this code under Windows Vista is not implemented.

Any improvements, comments or suggestions to this code are welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionhow to change the size of flake? Pin
eagle_chen27-Mar-12 2:29
eagle_chen27-Mar-12 2:29 
Rantgreat job! Pin
lanmuy26-Dec-10 20:35
lanmuy26-Dec-10 20:35 
GeneralIt doesn't work on XP ! Pin
gndnet27-Apr-08 4:20
gndnet27-Apr-08 4:20 
GeneralVery festive Pin
_awatts17-Dec-07 22:29
_awatts17-Dec-07 22:29 
GeneralRe: Very festive, can flakes be randomly drawn in front of active windows Pin
Igor Tolmachev18-Dec-07 22:29
Igor Tolmachev18-Dec-07 22:29 
QuestionNeat Pin
Hard Coder16-Dec-07 11:19
Hard Coder16-Dec-07 11:19 
GeneralRe: Neat, would it be hard to fix it so that the snow falls onto windows tops Pin
Igor Tolmachev18-Dec-07 22:20
Igor Tolmachev18-Dec-07 22:20 
GeneralCool - but it hates 4 monitors :) Pin
Ray Cassick15-Dec-07 18:50
Ray Cassick15-Dec-07 18:50 
Neat little app but when I run it on my 4 monitor setup two things happen.

1) It only snows on the 2 monitors to the left.

2) When the flakes cross the horizontal boundary between the two monitors and get to the lower one they start to streak and leave a 'snow pile' on the desk top Smile | :)

Darn, now I have to shovel Smile | :)

GeneralRe: Cool - but it hates 4 monitors :) Pin
Dave Cross16-Dec-07 4:29
professionalDave Cross16-Dec-07 4:29 
GeneralRe: Cool - I have got it working with 3 monitors [modified] Pin
MikeWilliams17-Dec-07 22:40
MikeWilliams17-Dec-07 22:40 
GeneralRe: Cool - I have got it working with 3 monitors Pin
Igor Tolmachev18-Dec-07 22:25
Igor Tolmachev18-Dec-07 22:25 
GeneralRe: Cool - I have got it working with 3 monitors Pin
MikeWilliams18-Dec-07 23:16
MikeWilliams18-Dec-07 23:16 
GeneralRe: Cool - but it hates 4 monitors :) Pin
MikeWilliams18-Dec-07 23:19
MikeWilliams18-Dec-07 23:19 
GeneralRe: Cool - but it hates 4 monitors :) Pin
Ray Cassick19-Dec-07 2:12
Ray Cassick19-Dec-07 2:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.