好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

通过监听Windows消息对复合控件进行整体控制

通过监听Windows消息对复合控件进行整体控制

在开发Winform复合控件时,有很多时候需要对控件进行整体的控制,比如监听鼠标的进入、移出而对控件进行渲染,对键盘事件时行控件,而这些对一个复合控件来说,实现起来是相当困难的,假如一个复合控件里面有10子控件,很难对每个控件进行事件监听,而且逻辑也不好做。在本人开发的控件中,起先曾试过对每个控件的事件时行监听,一大堆地代码和实现算法,最终也达不到效果,后来决定放弃这种做法。有一次,做一个模仿VS的IDE的设计器功能,最初没有探索到使用VS自带的DesignSurface的类,就通过监听Windows的消息来实现,完成后虽然没有VS的IDE设计器的效果好,但使用的感觉也比较接近(后几次改进,最终还是使用DesignSurface类实现一个通过的设计器,非常好的应用到所有项目中去),虽然设计器的最后没有使用到消息监听,但回过头想下以前的复合控件,如通过消息机制实现,既简单,又能完全解决需求,于是对现在的监听消息代码作重新设计,完成一个通用的对复合控件进行整体监听的一个功能类。主要思想是:

1、监听进程的有关的鼠标和键盘消息。

2、通过反射调用控件的标准的鼠标和键盘方法(保护方法,如:OnMouseMove、OnMouseEnter等)。

3、允许用户过滤部分消息。(参考Framework中相关事件参数中的Cancel属性)

主要实现:

实现 IMessageFilter 接口,同时订阅Applaction的消息。

代码

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Windows.Forms;
using  System.Reflection;
using  System.Drawing;

namespace  LC.MessageListen
{
     ///   <summary>
     ///  监听应用程序的消息,对复合控件的部分消息进行一个整体的控件,
     ///  允许控件订阅消息,自动执行控件的方法,包括私有方法等。
     ///  MessageListener会自动调用AddMessageFilter和移除,当控件销毁前应执行UnRegist方法。
     ///  父与子同时做订阅,如不能达到要求,可以父与子各创建一个MessageListener实例。
     ///   </summary>
     public   class  MessageListener : IMessageFilter
    {

复制代码

通过一个集合保存需要进行控件的复合控件

        Dictionary<int, RegistControlWrapper> m_RegistControls = new Dictionary<int, RegistControlWrapper>();

int 为控件的句柄,RegistControlWrapper封装了控件订阅了哪些事件。

订阅的事件种类规定如下:

代码

         const   string  _OnMouseHover  =   " OnMouseHover " ;
         const   string  _OnMouseWheel  =   " OnMouseWheel " ;
         const   string  _OnKeyDown  =   " OnKeyDown " ;
         const   string  _OnKeyUp  =   " OnKeyUp " ;
         const   string  _OnMouseEnter  =   " OnMouseEnter " ;
         const   string  _OnMouseLeave  =   " OnMouseLeave " ;
         const   string  _OnMouseDown  =   " OnMouseDown " ;
         const   string  _OnMouseUp  =   " OnMouseUp " ;
         const   string  _OnMouseMove  =   " OnMouseMove " ;
         const   string  _OnMouseClick  =   " OnMouseClick " ;
         const   string  _OnMouseDoubleClick  =   " OnMouseDoubleClick " ;

复制代码

两个公有方法,一个注册控件,和取消控件注册的方法

代码

         ///   <summary>
         ///  注册一个监听的控件。
         ///   </summary>
         ///   <param name="wrapper"></param>
         public   void  Regist(RegistControlWrapper wrapper)
        {
             lock  (m_lckObj)
            {
                m_RegistControls[wrapper.Handle]  =  wrapper;
                 if  (m_RegistControls.Count  ==   1 )
                {
                    Application.AddMessageFilter( this );
                }
            }
        }

         ///   <summary>
         ///  取消控件的监听消息。
         ///   </summary>
         ///   <param name="handle"></param>
         public   void  UnRegist( int  handle)
        {
             lock  (m_lckObj)
            {
                 if  (m_RegistControls.ContainsKey(handle))
                    m_RegistControls.Remove(handle);
                 if  (m_RegistControls.Count  <=   0 )
                {
                    Application.RemoveMessageFilter( this );
                }
            }
        }

复制代码

这两个方法根据集合是否为空自动增加和移除对进程消息的监听。

还有一个单例的方法        

public static MessageListener GetInstance()
        {
            lock (insLock)
            {
                if (instance == null)
                    instance = new MessageListener();
            }
            return instance;
        }

同时保留用户可能创建多实例的功能,这个在后来的应用中还是解决了其他的问题。

通过监听Windows消息对复合控件进行整体控制(C#)二

前篇说了对消息监听的一些粗的实现,现在具体说说消息监听:

在实现IMessageFilter接口的成员方法PreFilterMessage做处理。

在这里,实现需要监听的Windows消息只要有:

代码

         const   int  WM_KEYDOWN  =   0x100 ;
         const   int  WM_SYSKEYDOWN  =   0x104 ;
         const   int  WM_KEYUP  =   0x101 ;
         const   int  WM_SYSKEYUP  =   0x105 ;
         const   int  WM_MOUSEMOVE  =   0x200 ;
         const   int  WM_MOUSEHOVER  =   0x2a1 ;
         const   int  WM_MOUSELEAVE  =   0x2a3 ;
         const   int  WM_LBUTTONDOWN  =   0x201 ;
         const   int  WM_LBUTTONUP  =   0x202 ;
         const   int  WM_LBUTTONDBLCLK  =   0x203 ;
         const   int  WM_RBUTTONDOWN  =   0x204 ;
         const   int  WM_RBUTTONUP  =   0x205 ;
         const   int  WM_RBUTTONDBLCLK  =   0x206 ;
         const   int  WM_NCMOUSEMOVE  =   0xa0 ;
         const   int  WM_NCLBUTTONDOWN  =   0xa1 ;
         const   int  WM_NCLBUTTONUP  =   0xa2 ;
         const   int  WM_MOUSEWHEEL  =   0x20a ;

复制代码

同时很感谢大家提供的精彩对鼠标消息转换的代码

代码

         // ----------开源代码----------------------
         int  zDelta;

         private   const   int  MK_LBUTTON  =   0x0001 ;
         private   const   int  MK_RBUTTON  =   0x0002 ;
         private   const   int  MK_SHIFT  =   0x0004 ;
         private   const   int  MK_CONTROL  =   0x0008 ;
         private   const   int  MK_MBUTTON  =   0x0010 ;
         private   const   int  MK_XBUTTON1  =   0x0020 ;
         private   const   int  MK_XBUTTON2  =   0x0040 ;
         public   static   int  GetXLParam( int  lparam) {  return  LowWord(lparam); }
         public   static   int  GetYLParam( int  lparam) {  return  HighWord(lparam); }
         public   static   int  LowWord( int  word) {  return  word  &   0xFFFF ; }
         public   static   int  HighWord( int  word) {  return  word  >>   16 ; }
         public   static   int  GetWheelDeltaWParam( int  wparam) {  return  HighWord(wparam); }
         public   static  MouseButtons GetMouseButtonWParam( int  wparam)
        {
             int  mask  =  LowWord(wparam);

             if  ((mask  &  MK_LBUTTON)  ==  MK_LBUTTON)  return  MouseButtons.Left;
             if  ((mask  &  MK_RBUTTON)  ==  MK_RBUTTON)  return  MouseButtons.Right;
             if  ((mask  &  MK_MBUTTON)  ==  MK_MBUTTON)  return  MouseButtons.Middle;
             if  ((mask  &  MK_XBUTTON1)  ==  MK_XBUTTON1)  return  MouseButtons.XButton1;
             if  ((mask  &  MK_XBUTTON2)  ==  MK_XBUTTON2)  return  MouseButtons.XButton2;

             return  MouseButtons.None;
        }

复制代码

对消息的处理逻辑如下:

代码

         #region  IMessageFilter 成员

         public   bool  PreFilterMessage( ref  Message m)
        {
             /// /System.Diagnostics.Debug.WriteLine(m);
             bool  isCancel  =   false ;
             switch  (m.Msg)
            {
                 case  WM_MOUSEHOVER:
                    ProcessMouseHover(m.HWnd);
                     break ;
                 case  WM_MOUSEWHEEL:     // 鼠标滚轮
                    ProcessMouseWheel( ref  m,  ref  isCancel);
                     break ;
                 case  WM_LBUTTONDOWN:                    // 要处理MouseClick事件
                 case  WM_RBUTTONDOWN:
                    ProcessMouseDown( ref  m,  ref  isCancel);
                     break ;

                 case  WM_LBUTTONDBLCLK:                  // MouseDoubleClick事件
                 case  WM_RBUTTONDBLCLK:
                    ProcessMouseDBCLK( ref  m,  ref  isCancel);
                     break ;

                 case  WM_LBUTTONUP:
                 case  WM_RBUTTONUP:
                    ProcessMouseUp( ref  m,  ref  isCancel);
                     break ;

                 case  WM_MOUSEMOVE:
                    ShowCursor();                  // 显示光标。
                    ProcessMouseMove( ref  m,  ref  isCancel);
                     break ;

                 case  WM_MOUSELEAVE:
                    ProcessMouseLeave( ref  m,  ref  isCancel);
                     break ;

                 case  WM_SYSKEYDOWN:
                 case  WM_KEYDOWN:
                    ShowCursor();
                    ProcessKeyDown( ref  m,  ref  isCancel);
                     break ;
                 case  WM_SYSKEYUP:
                 case  WM_KEYUP:
                    ProcessKeyUp( ref  m,  ref  isCancel);
                     break ;
            }

             return  isCancel;
        }

         #endregion

复制代码

其他 ShowCursor(); 是一个附加的自动隐藏光标类的一个显示方法,当我们接收到鼠标或键盘的动作时,就必需要显示光标。

需要注意的事项:

作为一个事件的消息控件,当控件的某一子控件产生了消息,如果控件订阅了该消息,系统会自动调用控件相对应的方法,如OnMouseEnter等,如有需要对消息进行过滤,该消息就不会发往子控件了。对消息过滤也会产生副作用,如过滤了WM_MOUSELEAVE 消息后,当下一次鼠标再离开该子控件,就不会再发送鼠标离开的消息了,不是很Windows系统的消息机制,希望有经验的高手作下说明。

还有,没有鼠标进入的消息,只有鼠标移动的消息,因此,实现中要保存上一次鼠标移动的控件的标志,通过比较实现鼠标进入和移出事件。MouseUp消息也没有 wparam 也没有值,不能使用GetMouseButtonWParam方法。的MouseDown消息中要实现MouseClick事件,在MouseMove消息中实现MouseEnter,MouseLeave事件等。

至此一个对控件事先整体的消息控制功能完成了,该程序经测试通过,并应用到现有项目,效果非常好,只要应用在如浮动式窗口的自动隐藏、复合控件对鼠标进出而进行边框的渲染等,希望大家能挖掘出更多的应用,并留言说说。下面附上原码,其他有个自动隐藏光标的类,逻辑简单,不再累赘。

原码下载 /Files/Yjianyong/MessageListener.rar

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于通过监听Windows消息对复合控件进行整体控制的详细内容...

  阅读:70次

上一篇: Android游戏框架

下一篇:Ext.NET