65.9K
CodeProject 正在变化。 阅读更多。
Home

桌面飘雪!第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.59/5 (9投票s)

2007年12月15日

CPOL

1分钟阅读

viewsIcon

214737

downloadIcon

3984

本文档解释了如何创建一个在桌面飘雪的应用程序。

Screenshot - FallingSnow

引言

自从我发表上一篇文章以来,我收到了许多用户关于如何改进程序的询问。本文档演示了如何通过直接在桌面上下文中使用的绘图函数,创建飘落在桌面上的雪花。当应用程序启动时,它会创建一个雪花数组并启动每个雪花的计时器。对 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 下在图标后绘制雪花尚未实现。

欢迎对这段代码提出任何改进、评论或建议。

© . All rights reserved.