使用默认的CMenu菜单类或者继承CMenu实现的菜单扩展类,在显示的时候最外层都会有边框出现,或者说是具有3D外观(菜单阴影不算),当改变菜单背景色或者需要加个边框线时就会看上去很不美观。看过很多菜单的自定义实现类,一般可以有两种方式来实现外框的移除。
       第一种方法就是:自定义窗口,完全模拟菜单的实现,自给自足,倒是能够完全满足开发需要,不过实现的复杂让人头痛,此处略过不提。

下面介绍第二种比较简单直接的方法:安装钩子,在菜单创建时就改变其窗口属性。其实菜单应该也算是一个窗口类,不过实在是无从得知到底在哪创建的窗口,所以下下钩子,过程倒是明了许多。
 实现如下:

先在cpp前面申明一下:

static HHOOK g_hook=NULL; // 全局钩子
        static LRESULT WINAPI CallWndProc(int, WPARAM, LPARAM); // 安装的钩子的窗口过程
        static LRESULT WINAPI MenuWndProc(HWND, UINT, WPARAM, LPARAM); // 用来处理菜单的窗口过程

然后是钩子的实现:

/////////////////////////////////////////////////////////////////////////////
// 如果需要去除菜单的外部边框,需要通过安装钩子,设置外框属性并改变菜单大小
WNDPROC oldWndProc = NULL; // 用来保存被替换的窗口过程
LRESULT WINAPI CallWndProc(int code, WPARAM wParam, LPARAM lParam)
{
 CWPSTRUCT* pStruct = (CWPSTRUCT*)lParam;
 while (code == HC_ACTION)
 {
  HWND hWnd = pStruct->hwnd;
  // 捕捉创建消息WM_CREATE,后面筛选为是否是菜单的创建
  if ( pStruct->message != WM_CREATE)
   break;
  TCHAR sClassName[10];
  int Count = ::GetClassName(hWnd, sClassName, sizeof(sClassName)/sizeof(sClassName[0]));
  // 检查是否菜单窗口,#32768为菜单类名
  if ( Count != 6 ||  _tcscmp(sClassName, _T("#32768")) != 0 )   
   break;
  
  WNDPROC lastWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);   
  if (lastWndProc != MenuWndProc)   
  {   
   // 替换菜单窗口过程  
   SetWindowLong(hWnd, GWL_WNDPROC, (long)MenuWndProc);   
   // 保留原有的窗口过程   
   oldWndProc = lastWndProc;   
  }
  break;   
 }

return CallNextHookEx((HHOOK)WH_CALLWNDPROC, code, wParam, lParam); 
}

// 处理菜单的窗口过程
LRESULT WINAPI MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{   
 LRESULT lResult;
 switch (message)   
 {      
 case WM_CREATE:
  {   
   // 首先要去掉菜单窗口的一些扩展风格
   // 包括:WS_BORDER、WS_EX_DLGMODALFRAME、WS_EX_WINDOWEDGE
   lResult = CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);   
   DWORD dwStyle = ::GetWindowLong(hWnd,   GWL_STYLE);   
   DWORD dwNewStyle = (dwStyle & ~WS_BORDER);
   ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);   
   DWORD dwExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
   DWORD dwNewExStyle = (dwExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE));   
   ::SetWindowLong(hWnd, GWL_EXSTYLE, dwNewExStyle);   
   return lResult; 
  }   
 case   WM_PRINT: // 此处阻止非客户区地绘制
   return CallWindowProc( oldWndProc, hWnd, WM_PRINTCLIENT, wParam, lParam);

case   WM_WINDOWPOSCHANGING:   
  {   
   // 最后,由于我们在MeasureItem里指定了菜单大小,而系统会自动替菜单加边框,
   // 因此必须去掉此部分额外地尺寸,将菜单大小改小
   LPWINDOWPOS lpPos = (LPWINDOWPOS)lParam;   
   lpPos->cx -= 2*GetSystemMetrics(SM_CXBORDER)+4;   
   lpPos->cy -= 2*GetSystemMetrics(SM_CYBORDER)+4;
   lResult = CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);  
   return 0;
  }   
 case   WM_GETICON:
  return 0;    
 default:   
  return  CallWindowProc( oldWndProc, hWnd, message, wParam, lParam);   
 }   
}

/////////////////////////////////////////////////////////////

最后是调用:
下钩子需要调用SetWindowsHookEx,参数包括主窗口的实例句柄theApp.m_hInstance,可以在调用时作为参数传入,同时也设一个参数作为是否安装钩子的标识,以便在退出时判断是否需要卸载钩子(UnhookWindowsHookEx(g_hook))。示例如下:

void CSkinMenu::RemoveMenuBorder(HINSTANCE hInst, BOOL bRemove /* = TRUE */)
{
 m_bRemoveBorder = bRemove; // 标识

// 需要移除边框时,要安装钩子
 if (m_bRemoveBorder)
 {
  DWORD id = ::GetCurrentThreadId(); // 获取当前线程的ID
  g_hook = SetWindowsHookEx(WH_CALLWNDPROC,CallWndProc,hInst,id);
 }
}

这样子,就搞定菜单的边框了,最后要记得,如果安装了钩子,需要卸载掉。

【转载】CMenu自绘---钩子---去除边框的更多相关文章

  1. 【Android】Android EditText 去除边框

    [Android]Android EditText 去除边框 将EditText属性设置修改 android:background="@null" //////////////// ...

  2. Android Listview 去除边框

    最近在做一个时间轴的功能,因为原生效果的Listview有item分隔边框,所以就需要去除边框,调用listview的setDivider方法就可以了: listView.setDivider(nul ...

  3. WPF去除边框的方法

    原文:WPF去除边框的方法 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/yangsen600/article/details/81978125 W ...

  4. QT自绘标题和边框

    在QT中如果想要自绘标题和边框,一般步骤是: 1) 在创建窗口前设置Qt::FramelessWindowHint标志,设置该标志后会创建一个无标题.无边框的窗口. 2)在客户区域的顶部创建一个自绘标 ...

  5. golang-imagick图片去除边框(黑边,白边,),添加中文字

    图片去除边框(黑边,白边) package main import ( "fmt" "github.com/gographics/imagick/imagick" ...

  6. (webapp)微信和safri 对于html5 部分功能不兼容,多选或单选下拉框去除边框无效果。

    1 appearance:none; 2 -moz-appearance:none; /* Firefox */ 3 -webkit-appearance:none; /* Safari 和 Chro ...

  7. [css小技巧]input去除边框问题

    border:none;是不够的 (1)在谷歌浏览器添加 outline: none;去除点击后产生的边框; (2)IE7下border: none;还会有边框存在,改用border: 0;即可,同时 ...

  8. winform重绘控件边框

    首先添加一个用户控件 对于重绘边框有三个需要考虑的东西 1:是否显示边框 2:边框颜色 3:边框宽度 所以定义三个私有变量 /// <summary>/// 是否显示边框/// </ ...

  9. 去除input边框 input去除边框 去除input获取焦点时的蓝色外边框

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

随机推荐

  1. bzoj千题计划178:bzoj2425: [HAOI2010]计数

    http://www.lydsy.com/JudgeOnline/problem.php?id=2425 题意转化: 给定一个集合S,求S的全排列<给定排列 的排列个数 从最高位开始逐位枚举确定 ...

  2. .NET面试题系列(五)数据结构(Array、List、Queue、Stack)及线程安全问题

    常用数据结构的时间复杂度 如何选择数据结构 Array (T[]) 当元素的数量是固定的,并且需要使用下标时. Linked list (LinkedList<T>) 当元素需要能够在列表 ...

  3. html5 canvas 垂直渐变描边

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. 第9月第26天 pairs和ipairs cocos2dx 动画

    1. a={ ip = "127.0.0.1", port = 6789 } for i,v in pairs(a) do print(i,v) end a={1} for i,v ...

  5. mybatis延迟加载——(十二)

    1.     什么是延迟加载 resultMap可以实现高级映射(使用association.collection实现一对一及一对多映射),association.collection具备延迟加载功能 ...

  6. 转:存储之直连存储Dell Powervault MD 3000

    存储之直连存储DellPowervault MD 3000 存储根据服务器类型可以分为:封闭系统的存储和开放系统的存储 1.封闭系统的存储:封闭系统主要指大型机,AS400等服务器 2.开放系统的存储 ...

  7. 【Python】Flask系列-模板笔记

    Jinja2模板 Jinja2模板传参 如何渲染模板: 模板放在templates文件夹下 从flask中导入render_template函数. 在视图函数中,使用render_template函数 ...

  8. Windows命令-系统木马取样

    1.前言 工作中偶尔会遇到去现场提取木马样本回公司分析的情况.如果是生产环境下,不方便安装各类抓包.安全软件时.能用系统自带的命令去定位出木马程序相关的信息是最理想不过的状态. 2.Windows常用 ...

  9. oracle日期、转换函数

    转换函数 日期类型转换成字符类型 select to_char(sysdate) s1, --14-3月 -16        to_char(sysdate, 'yyyy-mm-dd') s2, - ...

  10. 洛谷P2613有理数取余

    传送门 #include <iostream> #include <cstdio> #include <cstring> #include <algorith ...