zoukankan      html  css  js  c++  java
  • 窗口阴影效果

    背景:

    win7系统中可以在设置窗口底部有阴影效果,这样使得窗口看起来更有立体感。如果我们自定义窗口,没有用到系统默认的边框,这时阴影效果也随着没有了。我们需要在这样的窗口上加上阴影效果。

    方法:

    1)创建一个WS_EX_TRANSPARENT样式的窗口,该类窗口具有鼠标穿透的效果(这样的效果也可以用SetWindowRgn实现,但是我们这里比较特殊,只能使用前者)。

    2)将窗口绘制成阴影,这个可以用GDI+来实现。

    3)跟随目标窗口,这里需要处理这些消息:show、hide、minimize、maximize、paint、resize。这里采用的做法是hook目标窗口的窗口处理函数。这比编写代码是接受目标窗口发送来的消息更加方便,而且对于目标窗口来说阴影窗口是透明的。

    实现步骤:

    1)首先在窗口类中加入我们定义的阴影类对象:

    CShadowWndBase m_shadowWnd;

    2)我们在OnCreate(OnInitDlg)中将目标窗口的窗口句柄传给阴影窗口类对象,然后创建阴影窗口:

    m_shadowWnd.SetShadowWnd(*this);
    m_shadowWnd.Create(NULL,_T(""),WS_OVERLAPPEDWINDOW,CRect(0,0,0,0),GetDesktopWindow(),0);

    阴影窗口创建过程还有一个工作要做,就是要hook目标窗口的窗口处理函数,注意这里需要保存原来目标窗口的处理函数地址,因为我们会在新处理函数中调用旧函数

    int CShadowWndBase::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CWnd::OnCreate(lpCreateStruct) == -1)
            return -1;

        ASSERT(m_hWndShadow!=NULL);
        // TODO:  在此添加您专用的创建代码
        m_szShadowWindows.insert( make_pair(m_hWndShadow, this) );

        ModifyStyle(WS_MAXIMIZEBOX | WS_SIZEBOX | WS_CAPTION | WS_DLGFRAME, 0, 0);
        ::SetWindowLong(*this,GWL_EXSTYLE, GetWindowLong(*this,GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);
        ModifyStyleEx(WS_EX_TOPMOST, WS_EX_NOACTIVATE);

        // Set parent window original processing.
        m_OriParentProc = ::GetWindowLong(m_hWndShadow, GWL_WNDPROC);
        ::SetWindowLong(m_hWndShadow, GWL_WNDPROC, (LONG)ShadowProc);

        return 0;
    }

    3)

    这里还有一些问题,新处理函数是静态函数,无法访问非静态成员变量;另外还要考虑到另外的情况,可能同一进程中有多个窗口使用了阴影窗口类,我们也需要记录目标窗口和阴影窗口的对应关系。只需要定义一个类静态变量(其作用在这里就相当于一个全局变量):

    static std::map<HWND, CShadowWndBase*>  m_szShadowWindows;

    4)这样对于目标窗口的一些消息就可以直接处理,然后再传给默认函数。下面是新处理函数:

    LRESULT CALLBACK CShadowWndBase::ShadowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
    {
        // Find the shadow window pointer via parent window handle.
        ASSERT( m_szShadowWindows.find(hwnd) != m_szShadowWindows.end() );
        CShadowWndBase *pThis    = m_szShadowWindows[hwnd];
        WNDPROC pDefProc        = (WNDPROC)pThis->m_OriParentProc;

        switch(uMsg)
        {
        case WM_ERASEBKGND:
        case WM_PAINT:
        case WM_MOVE:
        case WM_ACTIVATE:
        case WM_NCACTIVATE:{
                if (::IsWindowVisible(hwnd)){
                    pThis->AdjustWindowPos();
                }
                break;
            }
        case WM_DESTROY:{
                // Destroy the shadow window.
                pThis->DestroyWindow();   
                break;
            }
        case WM_NCDESTROY:{
                // Remove shadow window from map.
                m_szShadowWindows.erase(hwnd);   
                break;
            }
        case WM_SHOWWINDOW:{
                // the window is being hidden
                if (!wParam){
                    pThis->ShowWindow(SW_HIDE);
                }
                else{
                    CRect rect;
                    pThis->GetWindowRect(rect);
                    pThis->ShowWindow(SW_SHOW);
                }
                break;
            }
        default:
            break;
        }

        return pDefProc(hwnd, uMsg, wParam, lParam);//注意最后将消息都给原来的处理函数,让其保持原状(至少是外表上的)。
    }

    5)窗口在检测到目标窗口尺寸或位置改变时需要相应的改变:

    void CShadowWndBase::AdjustWindowPos()
    {
        CRect rcParent;

        ::GetWindowRect(m_hWndShadow, &rcParent);
        rcParent.InflateRect(m_nShadowSize, m_nShadowSize);
        rcParent.MoveToXY(rcParent.left+2,rcParent.top+2);
        ::SetWindowPos(*this, m_hWndShadow, rcParent.left,rcParent.top,rcParent.Width(), rcParent.Height(), SWP_NOACTIVATE);

        GetClientRect(m_rcClient);

        // If window was resized, redraw the shadow.
        if (m_bResize == TRUE)
        {
            m_bResize = FALSE;
            DrawShadow();
        }
    }

    6)利用GDI+绘制阴影效果:

    void CShadowWndBase::DrawShadow()
    {

        // Create shadow path.
        Rect rcShadow(m_rcClient.left, m_rcClient.top, m_rcClient.Width(), m_rcClient.Height());
        m_ShadowPath.CreateRoundRect(rcShadow, 18);

        Bitmap bitmap(rcShadow.Width, rcShadow.Height);
        Graphics graphics(&bitmap);

        // Create shadow brush.
        PathGradientBrush brShadow(m_ShadowPath.m_pPath);
        Color clrShadow[] = {Color::Transparent, Color(255, 0, 0, 0)};
        int nCount = 2;

        REAL szPos[] = {0.0F, 1.0F};
        brShadow.SetInterpolationColors(clrShadow, szPos, nCount);
        brShadow.SetFocusScales((REAL)(rcShadow.Width-6*m_nShadowSize)/(rcShadow.Width), (REAL)(rcShadow.Height-6*m_nShadowSize)/(rcShadow.Height));
        // Draw shadow.
        rcShadow.Inflate(-m_nShadowSize-6,-m_nShadowSize-6);
        graphics.ExcludeClip(rcShadow);
        graphics.FillPath(&brShadow, m_ShadowPath.m_pPath);

        // Update layered window.
        HBITMAP hBitMap;
        m_pBitmap.GetHBITMAP((ARGB)Color::Black, &hBitMap);
        CBitmap bitmap;
        bitmap.Attach(hBitMap);

        CClientDC dc(this);

        CDC dcCompat;
        dcCompat.Attach(::CreateCompatibleDC(NULL));
        dcCompat.SelectObject(bitmap);

        BITMAP bmpInfo = {0};
        bitmap.GetBitmap(&bmpInfo);
        CSize size(bmpInfo.bmWidth, bmpInfo.bmHeight);
        CRect rcWindow;
        GetWindowRect(&rcWindow);

        BLENDFUNCTION bf = { AC_SRC_OVER, 0, 128, AC_SRC_ALPHA };

        CPoint ptWnd(rcWindow.left, rcWindow.top);
        CPoint ptSource(0, 0);

        ::UpdateLayeredWindow(m_hWnd, dc.m_hDC, &ptWnd, &size,
            dcCompat.m_hDC, &ptSource, 0, &bf, ULW_ALPHA);
    }

  • 相关阅读:
    android 工具类 DateUtil
    POJ1580 水题,积累!
    POJ1159,Palindrome
    iOS开发UI篇章 15-项目中的常见文件
    MongoDB:Map-Reduce
    三层架构下实现用户登陆C#
    Inno Setup 安装inf文件的一个例子
    delphi 主线程向子线程发送消息
    PeekMessage和GetMessage函数的主要区别
    delphi SPCOMM的一些用法注意
  • 原文地址:https://www.cnblogs.com/aishangxue/p/3523163.html
Copyright © 2011-2022 走看看