AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > VC编程

利用钩子实现菜单阴影效果

51自学网 2015-08-30 http://www.wanshiok.com

 

下面就看如何慢慢实现这些消息的响应函数吧: void CMenuWndHook::OnWindowPosChanging(WINDOWPOS *pWindowPos)
{
if (!IsShadowEnabled())
{
 //加一块区域来显示阴影-------
 pWindowPos->cx += 4;
 pWindowPos->cy += 4;
}
  
// 为了绘制阴影,我们须要先保存这个区域的图像,以便绘制半透明的阴影.
if (!IsWindowVisible(m_hWnd) && !IsShadowEnabled())
{
 if (m_bmpBack.m_hObject != NULL)
 {
  m_bmpBack.DeleteObject();
 }
    m_bmpBack.Attach(GetScreenBitmap(CRect(pWindowPos->x,
                       pWindowPos->y,
          pWindowPos->cx,
   pWindowPos->cy)));
}
}
 
 
void CMenuWndHook::OnNcCalcsize(NCCALCSIZE_PARAMS* lpncsp)
{
if (!IsShadowEnabled())
{
 //留出一点区域来显示阴影-------
 lpncsp->rgrc[0].right -= 4;
 lpncsp->rgrc[0].bottom -= 4; 
  }
}

上面我用到了两个全局函数, 其中IsShadowEnabled是检测系统是否开启了菜单阴影(主要针对于Windows XP, Windows 2003及他更高的版本) 如果系统已经给我们开启了阴影,我们还忙乎什么哦。BOOL WINAPI IsShadowEnabled()
{
BOOL bEnabled = FALSE;
if (SystemParametersInfo(SPI_GETDROPSHADOW, 0, bEnabled,0))
{
 return bEnabled;
}
return FALSE;
}

其中 SPI_GETDROPSHADOW 在VC6里面没有被声明,你需要自已声明它:
#ifndef SPI_GETDROPSHADOW
#define SPI_GETDROPSHADOW 0x1024
#endif

另外还有 GetScreenBitmap 函数用于截取屏幕上指定区域内的图像: HBITMAP WINAPI GetScreenBitmap (LPCRECT pRect)
{
  HDC   hDC;
  HDC   hMemDC;
  HBITMAP hNewBitmap = NULL;

  if ((hDC = ::GetDC(NULL)) != NULL )
  {
    if ((hMemDC = ::CreateCompatibleDC(hDC)) != NULL)
    {
      if ((hNewBitmap = ::CreateCompatibleBitmap(hDC,
           pRect->right - pRect->left,
           pRect->bottom - pRect->top)) != NULL)
      {
        HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hMemDC, hNewBitmap);
        ::BitBlt(hMemDC, 0, 0, pRect->right - pRect->left, pRect->bottom - pRect->top,
   hDC, pRect->left, pRect->top, SRCCOPY);
        ::SelectObject(hMemDC, (HGDIOBJ)hOldBitmap);
      }
      ::DeleteDC(hMemDC);
    }
    ::ReleaseDC(NULL, hDC);
  }
  return hNewBitmap;
}

下面这两个函数要做的事就差不多了: void CMenuWndHook::OnNcPaint()
{
  CWindowDC dc(CWnd::FromHandle(m_hWnd));
OnPrint(&dc);
}

void CMenuWndHook::OnPrint(CDC *pDC)
{
CRect rc;
GetWindowRect(m_hWnd, &rc);
  rc.OffsetRect(-rc.TopLeft());

// 绘制阴影
if (!IsShadowEnabled())
{
 CDC cMemDC;
 cMemDC.CreateCompatibleDC (pDC);
 HGDIOBJ hOldBitmap = ::SelectObject (cMemDC.m_hDC, m_bmpBack);
 pDC->BitBlt (0, rc.bottom - 4, rc.Width() - 4, 4, &cMemDC, 0, rc.bottom - 4, SRCCOPY);
 pDC->BitBlt (rc.right - 4, 0, 4, rc.Height(), &cMemDC, rc.right - 4, 0, SRCCOPY);
 
 DrawShadow(pDC, rc);
 rc.right -= 4;
 rc.bottom -= 4;
}

// 绘制边框
pDC->Draw3dRect(rc, m_crFrame[0], m_crFrame[1]);
rc.DeflateRect (1, 1);
pDC->Draw3dRect(rc, m_crFrame[2], m_crFrame[3]);

}

  在指定的矩形区域内绘制阴影的全局函数(当然这些函数不一定都要做成全局函数,我把它们写成了全局函数是因为在好几个类中都用到了它们, 写成全局函数便于调用) 也许你会觉得这不符合面向对象编程的思想,其实面向过程的编程思想,并不一定就比面向对象的思想落后,我把这些比较独立的函数写成全局函数,当作API函数用,还是觉得很方便的,如果硬要将它们塞到一个类里面,反而觉得很郁闷 。:-). void DrawShadow(CDC *pDC, CRect rect);
void DrawShadow(CDC *pDC, CRect rect)
{
COLORREF oldcolor = RGB(255, 255, 255);
BYTE newValR, newValG, newValB;
BYTE AlphaArray[] = {140, 170, 212, 240};
BYTE AlphaArray2[] = {170, 205, 220, 240, 240, 250, 255};

// 底部的阴影 -----------------------------------------
int i, j;
for (j = 0; j < 4; j++)
{
 for (i = 6; i <= rect.right - 5; i++)
 {
  oldcolor = pDC->GetPixel(i, rect.bottom - (4 - j));
  newValR = GetRValue(oldcolor) * AlphaArray[j] / 255; 
  newValG = GetGValue(oldcolor) * AlphaArray[j] / 255; 
  newValB = GetBValue(oldcolor) * AlphaArray[j] / 255; 
  pDC->SetPixel(i, rect.bottom - (4 - j), RGB(newValR, newValG, newValB));
 }
}

// 右边的阴影 -----------------------------------------
for (i = 0; i < 4; i++)
{
 for (j = 6; j <= rect.bottom - 5; j++)
 {
  oldcolor = pDC->GetPixel(rect.right - (4 - i), j);
  newValR = GetRValue(oldcolor) * AlphaArray[i] / 255; 
  newValG = GetGValue(oldcolor) * AlphaArray[i] / 255; 
  newValB = GetBValue(oldcolor) * AlphaArray[i] / 255; 
  pDC->SetPixel(rect.right - (4 - i), j, RGB(newValR, newValG, newValB));
 }
}

// 角上的阴影 --------------------------------------
for (i = 0; i < 4; i++)
{
 for (j = 0; j < 4; j++)
 {
  if ((i + j) > 6) break;
 
  oldcolor = pDC->GetPixel(rect.right - 4 + i, rect.bottom - 4 + j);
  newValR = GetRValue(oldcolor) * AlphaArray2[i + j] / 255; 
  newValG = GetGValue(oldcolor) * AlphaArray2[i + j] / 255; 
  newValB = GetBValue(oldcolor) * AlphaArray2[i + j] / 255; 
  pDC->SetPixel(rect.right - 4 + i,
   rect.bottom - 4 + j,
   RGB(newValR,
   newValG,
   newValB));
 
  oldcolor = pDC->GetPixel(rect.right - 4 + i, rect.top + 5 - j);
  newValR = GetRValue(oldcolor) * AlphaArray2[i + j] / 255; 
  newValG = GetGValue(oldcolor) * AlphaArray2[i + j] / 255; 
  newValB = GetBValue(oldcolor) * AlphaArray2[i + j] / 255; 
  pDC->SetPixel(rect.right - 4 + i,
         rect.top + 5 - j,
         RGB(newValR,
         newValG,
         newValB));
 
  oldcolor = pDC->GetPixel(rect.left - i + 5, rect.bottom - 4 + j);
  newValR = GetRValue(oldcolor) * AlphaArray2[i + j] / 255; 
  newValG = GetGValue(oldcolor) * AlphaArray2[i + j] / 255; 
  newValB = GetBValue(oldcolor) * AlphaArray2[i + j] / 255; 
  pDC->SetPixel(rect.left - i + 5,
         rect.bottom - 4 + j,
         RGB(newValR,
         newValG,
         newValB));
 }
}
}

这么复杂? 唉! 还不是想让它把阴影画得更好看一点, 速度?...在我机子上还过得去。毕竟菜单是不会被频繁地重画的. 这样实现阴影确实有点笨拙,且在意外的时候可能会出现一些不愉快的绘图上的bug. 但是要实现Windows XP 那样完美的菜单阴影还是很难的。我希望已经知道的高手,能指点指点! 谢了先。
下面是处理清理工作了: void CMenuWndHook::OnNcDestroy()
{
delete this; // 自杀!
}

void CMenuWndHook::OnShowWindow(BOOL bShow)
{
  if (!bShow)
  {
    delete this; // 自杀2!
  }
}


... ..., 好狠哦! 嘿嘿!

扫尾工作还由是~CMenuWndHook它老人家做, 在delete自己的时候会自动调用它的: CMenuWndHook::~CMenuWndHook()
{
WNDPROC oldWndProc = (WNDPROC)::GetProp(m_hWnd, CoolMenu_oldProc);
if (oldWndProc != NULL)
{
 ::SetWindowLong(m_hWnd, GWL_WNDPROC,(DWORD)(ULONG)oldWndProc);
 ::RemoveProp(m_hWnd, CoolMenu_oldProc); 
}
m_WndMenuMap.RemoveKey(m_hWnd);
if (m_bmpBack.m_hObject != NULL) 
{
 m_bmpBack.DeleteObject();
}
}

这个类基本上写完了,如果我还有什么没讲清的地方,你就再去看看我的源代码吧。我们可以在APP类里面调用它: ............

#include "MenuWndHook.h"

...........


BOOL CNewmenuApp::InitInstance()
{
.......


CMenuWndHook::InstallHook();
}


int CNewmenuApp::ExitInstance()
{
CMenuWndHook::UnInstallHook();
return CWinApp::ExitInstance();
}

我时常听见人说 Delhpi 程序界面比VC程序的界面如何如何好? 如果是说默认的那些控件的外观,VC确实不如Delphi,(微软也真小气,自已产品的界面做得那么"华丽"(像Office XP/2003, Windows XP,VS.NET...), 而给我们用的这些控件的外观却这么"老土")...总之真正的精美的有个性的界面是大家自已做出来的,这正是我钟爱VC的理由之一。呵呵。

 
 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。

上一篇:检验EXE文件的完整性  下一篇:Win32&nbsp;核心&nbsp;DPC&nbsp;设计思想和实现思路浅析