装饰你的Title Bar

发表于:2007-07-14来源:作者:点击数: 标签:
Windows 98的发布给热衷于UI的用户带来了福音,它内嵌的Plus!,动态 弹出的菜单与ToolTip,更有那颜色渐变的Title Bar给我们增添了许多的乐趣。 其实即便是在Windows 95下也能使你的程序的Title Bar更具有个人魅力,在 Norton Utilities for 95中就有了颜色
  Windows 98的发布给热衷于UI的用户带来了福音,它内嵌的Plus!,动态
弹出的菜单与ToolTip,更有那颜色渐变的Title Bar给我们增添了许多的乐趣。
其实即便是在Windows 95下也能使你的程序的Title Bar更具有个人魅力,在
Norton Utilities for 95中就有了颜色渐变的Title Bar,在大量的Delphi 3.0
的第三方控件中更有提供了此类完整功能的控件。当然用控件可以快速开发漂亮的
程序,但对于爬键盘的人来说,了解程序内核的机理并且做出更Cool的Title Bar
才是最爽的事!本文列举了用代码装饰你的Title Bar的几种方法。
 1、修改Registry库

  在Windows 9x的桌面中,进入Display Properties对话框中的Appearance属性
页,可以修改Title Bar的字体的宽度与颜色。实际上所有这些更改都进入了
Registry库的HKEY-CURRENT-USER/Control Panel下。由于都是单纯的数字,
对于字体是不好修改的,但若是单纯修改颜色值,则在Control Panel的Colors下有
明显的value Name与value Data的含义。例如在Windows 98中,value Name为
ActiveTitle,value Data为“0 0 128”;value Name为GradientActiveTitle,
value Data为“168 200 240”,即表示活动时的Title Bar颜色由深蓝色渐变到浅
蓝色。值的含义很明显即为RGB的值。用Win32 SDK中的修改Registry库的API修改各
项意义明显的Color值,别忘了最后发送WM-SYSCOLORCHANGE消息给自己的窗口,
来验证改变后的效果。

  此种方法的好处是思路简单,并且下次重启Windows后,所有窗口均是改变后的
颜色,但是方法有些勉强且功能不强。


  2、利用SetSystem Color函数


  SetSystemColor的解释请参考相应手册,不再详述。这里仅列出一段代码片段,
示意将Windows背景改为黑色,将Windows中的文字改为绿色。

  int aiDsp[2];
  DWORD aRgb[2];
  aiDsp[0]=COLOR-WINDOW;
  aRgb[0]=RGB(0, 0, 0);
  aiDsp[1]=COLOR-WINDOWTEXT;
  aRgb[1]=RGB(0, 255, 0);
  SetSysColors(2, aiDsp, aRgb);

  SetSysColors会自动给所有Windows发送WM-SYSCOLORCHANGE消息向所有Window
声名系统颜色改变,但是并不改变注册库,因为下次重启Windows后,系统颜色又恢复原样。

  本方法实现简单,但影响了其他窗口特性,且功能太少。


  3、拿起你的刷(brush),握住你的笔(pen),在DC上尽情地想画什么就画什么


  在Windows 98下用VC 5.0生成小的Demo,在Windows 95下运行也正常。   下面先了解一下Windows重画非客户区的过程。在处理WM-NCPAINT、WM-NCACTIVE、
WM-SYSCOMMAND、WM-SETTEXT消息之后,Windows调用缺省处理消息函数DefWindowProc,
在此函数中将对非客户区进行重画操作,故而在CWnd的虚函数DefWindowProc中,
重画Title Bar,就可以达到我们的目的,但是若不对消息进行一定的过滤,势必引
起过多的重画,我们假定Title Bar上没有System Menu,即没有最大、最小和关闭按
钮在Title Bar上(见代码片段1)。这样可以简化操作。对消息的过滤与重画操作见
代码片段2。

  代码片段1:

  BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
  {
   cs.style &=~WS-SYSMENU; //取消Title Bar上的按钮
   return CFrameWnd::PreCreateWindow(cs);
  }

  代码片段2:

  LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  {
   LRESULT lrst=CFrameWnd::DefWindowProc(message, wParam, lParam);
   if (!::IsWindow(m-hWnd))
   return lrst;
   if (message=WM-NCPAINT ||message=WM-NCACTIVATE ||message=WM-NOTIFY)
   {
   CDC? pWinDC=GetWindowDC();
   if (pWinDC)
   DrawTitleBar(pWinDC);
   ReleaseDC(pWinDC);
   }
   return lrst;
  }

  在DrawTitleBar函数中,我们将采用乾坤大挪移,将Icon画到了右边,将最小、
最大、关闭按钮画到了左边,并画上了颜色渐变的Title Bar,在中间写了“My Own
Cool Title Bar!!!”的标题(见代码片段3)。最后将最小、最大、关闭按钮连上
了各自的消息(见代码片段4)。

  代码片段3:

  void CMainFrame::DrawTitleBar(CDC* pDC)
  {
   if (m-hWnd)
   {
   CRect rtWnd, rtTitle, rtButtons;
   GetWindowRect(&rtWnd); //整个Window的相对于屏幕的矩形
   //取得整个Title bar的矩形
   rtTitle.left=GetSystemMetrics(SM-CXFRAME);
   rtTitle.top=GetSystemMetrics(SM-CYFRAME);
   rtTitle.right=rtWnd.right-rtWnd.left-GetSystemMetrics(SM-CXFRAME);
   rtTitle.bottom=rtTitle.top+GetSystemMetrics(SM-CYSIZE);
  //重画颜色渐变的Title Bar;有DC,有矩形,想怎么画就怎么画
   DrawGradientBar(pDC, rtTitle); //此函数源码因篇幅略去
   //重画icon
   HICON hIcon=(HICON)::GetClassLong(m-hWnd, GCL-HICON);
   m-rtIcon.left=rtTitle.right-GetSystemMetrics(SM-CYSMICON);
   m-rtIcon.top=rtTitle.top+1;
   m-rtIcon.right=m-rtIcon.left+GetSystemMetrics(SM-CXSMICON);
   m-rtIcon.bottom=m-rtIcon.top+GetSystemMetrics(SM-CYSMICON);
   ::DrawIconEx(pDC->m-hDC, m-rtIcon.left, m-rtIcon.top,hIcon, GetSystemMetrics
(SM-CXSMICON), GetSystemMetrics(SM-CYSMICON), 0, NULL,
DI-NORMAL);
   m-rtIcon.OffsetRect(rtWnd.TopLeft()); //记录Icon屏幕位置
   //重画最小button
   int nButtHeight=GetSystemMetrics(SM-CYSMSIZE)-3;
  rtButtons.left=rtTitle.left;
   rtButtons.top=rtTitle.top+(GetSystemMetrics(SM-CYSIZE)-nButtHeight)/2;
  rtButtons.right=rtButtons.left+GetSystemMetrics(SM-CXSMSIZE);
  rtButtons.bottom=rtButtons.top+nButtHeight;
   pDC->DrawFrameControl(&rtButtons, DFC-CAPTION, DFCS-CAPTIONMIN);
   m-rtButtMin=rtButtons;
   m-rtButtMin.OffsetRect(rtWnd.TopLeft()); //记录最小button屏幕位置
   //重画最大或恢复button
   rtButtons.left=rtButtons.right;
   rtButtons.right=rtButtons.left+GetSystemMetrics(SM-CXSMSIZE);
   pDC->DrawFrameControl(&rtButtons, DFC-CAPTION, IsZoomed() ?
DFCS-CAPTIONRESTORE : DFCS-CAPTIONMAX);
   m-rtButtMax=rtButtons;
   m-rtButtMax.OffsetRect(rtWnd.TopLeft());//记录button屏幕位置
   //重画关闭button
   rtButtons.left=rtButtons.right;
   rtButtons.right=rtButtons.left+GetSystemMetrics(SM-CXSMSIZE);
   pDC->DrawFrameControl(&rtButtons, DFC-CAPTION, DFCS-CAPTIONCLOSE);
   m-rtButtExit=rtButtons;
   m-rtButtExit.OffsetRect(rtWnd.TopLeft())//记录关闭button屏幕位置;
   //重画caption
   int nOldMode=pDC->SetBkMode(TRANSPARENT);
   COLORREF clOldText=pDC->SetTextColor(RGB(0, 0, 0));
   pDC->SelectStockObject(ANSI-FIXED-FONT); rtTitle.right-=GetSystemMetrics
(SM-CYSMICON); pDC->DrawText((LPSTR)″My Own Cool Title
Bar!!!″, -1, &rtTitle, DT-CENTER);
   pDC->SetBkMode(nOldMode);
   pDC->SetTextColor(clOldText);
}
  }

  代码片段4:

  void CMainFrame::OnNcLButtonDown(UINT nHitTest, CPoint point)
  {
   //处理缺省操作,诸如双击Title Bar等其他动作
   Default();
   //检测最小,最大和关闭按钮是否按到
   if (m-rtButtExit.PtInRect(point))
   SendMessage(WM-CLOSE);
   else if (m-rtButtMin.PtInRect(point))
   SendMessage(WM-SYSCOMMAND, SC-MINIMIZE, MAKELPARAM(point.x, point.y) );
   else if (m-rtButtMax.PtInRect(point))
   {
   if (IsZoomed())
   SendMessage(WM-SYSCOMMAND, SC-RESTORE, MAKELPARAM(point.x, point.y));
   else
   SendMessage(WM-SYSCOMMAND, SC-MAXIMIZE, MAKELPARAM(point.x, point.y) );
   }
  }

  这里需要补充一点,若要程序更健壮,需要监视WM-WININICHANGED消息,因
为用户可能在别处动态地改变Title Bar的宽度及其他宽度,此时需要重新取得Title
Bar的各项新值,使得Title Bar重画。

  实际上有了DC,有了矩形,的确是可以随心所欲了,但是有了独创就一定有付出。
要完成彻底的乾坤大挪移,还需要在移动窗口后,更新最小、最大和关闭按钮的位置;
模拟按钮按下的动作;点击Icon后生成System Menu,并弹出,代价是大了一些。

  有了这种方法后,就完全没有必要非要和Windows对着干了,你可以设计自己的
Title Bar、自己的最小、最大和关闭按钮,在Title Bar上贴上喜欢的位图,使
Title Bar完全个性化。现在握住你的笔(pen),拿起你的刷(brush),尽情地装饰你
的Title Bar吧!

原文转自:http://www.ltesting.net