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

翻译|其它|编辑:郝浩|2006-03-08 11:40:00.000|阅读 1716 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

上面我用到了两个全局函数, 其中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的理由之一。呵呵。


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP