轉帖|其它|編輯:郝浩|2012-01-29 23:46:47.000|閱讀 2226 次
概述:前面兩篇文章講過《通過監聽Windows消息對復合控件進行整體控制》,講述了通過FrameWork框架提供的技術監聽Windows消息來實現事件的路由,但部分實現并不是很好,而且有部分功能并不能很好解決控件的事件,此篇通過對原方法進行改寫,有些實現通過調用Windows API輔助解決,基本上解決了控件的鍵盤和鼠標事件的路由。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
前面兩篇文章講過《通過監聽Windows消息對復合控件進行整體控制》,講述了通過FrameWork框架提供的技術監聽Windows消息來實現事件的路由,但部分實現并不是很好,而且有部分功能并不能很好解決控件的事件,此篇通過對原方法進行改寫,有些實現通過調用Windows API輔助解決,基本上解決了控件的鍵盤和鼠標事件的路由。(JS和WPF有事件路由的功能)
實現 IMessageFilter 接口 和 注冊控件的鼠標和鍵盤事件等對外接口請參考《通過監聽Windows消息對復合控件進行整體控制(C#)一 》,內部的實現通過使用Windows API 使代碼更嚴謹和清晰。
使用到的部分API方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace LC.Windows.MessageListen
{
class Win32API
{
//static public long Count;
#region 常量及開源
public const int WM_KEYDOWN = 0x100;
public const int WM_SYSKEYDOWN = 0x104;
public const int WM_KEYUP = 0x101;
public const int WM_SYSKEYUP = 0x105;
public const int WM_MOUSEMOVE = 0x200;
public const int WM_MOUSEHOVER = 0x2a1;
public const int WM_MOUSELEAVE = 0x2a3;
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_LBUTTONUP = 0x202;
public const int WM_LBUTTONDBLCLK = 0x203;
public const int WM_RBUTTONDOWN = 0x204;
public const int WM_RBUTTONUP = 0x205;
public const int WM_RBUTTONDBLCLK = 0x206;
public const int WM_NCMOUSEMOVE = 0xa0;
public const int WM_NCLBUTTONDOWN = 0xa1;
public const int WM_NCLBUTTONUP = 0xa2;
public const int WM_MOUSEWHEEL = 0x20a;
public const int WM_PAINT = 0xf;
public const int WM_TIMER = 0x113;
//----------開源代碼----------------------
int zDelta;
public const int MK_LBUTTON = 0x0001;
public const int MK_RBUTTON = 0x0002;
public const int MK_SHIFT = 0x0004;
public const int MK_CONTROL = 0x0008;
public const int MK_MBUTTON = 0x0010;
public const int MK_XBUTTON1 = 0x0020;
public 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;
}
public MouseButtons GetMouseUpMouseButtons(int msg)
{
if (msg == WM_LBUTTONUP)
return MouseButtons.Left;
else if (msg == WM_RBUTTONUP)
return MouseButtons.Right;
else
return MouseButtons.None;
}
public const int CWP_ALL = 0x0000;
public const int CWP_SKIPINVISIBLE = 0x0001;
public const int CWP_SKIPDISABLED = 0x0002;
public const int CWP_SKIPTRANSPARENT = 0x0004;
public const uint TME_HOVER = 0x00000001;
public const uint TME_LEAVE = 0x00000002;
public const uint TME_QUERY = 0x40000000;
public const uint TME_CANCEL = 0x80000000;
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int ShowCursor(bool isShow);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int GetCapture();
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SetCapture(IntPtr hWnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool GetCursorPos(ref POINT pos); //如果POINT是class 就不能加ref
//取出客戶端的矩形,相對于控件自身。
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool GetClientRect(int hwnd, [In, Out]ref RECT rect);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool PtInRect(ref RECT rect, POINT pos);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int ChildWindowFromPointEx(int hwnd, POINT pos, uint un);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool ClientToScreen(int hwnd, ref POINT pos);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool ScreenToClient(int hwnd, ref POINT pos);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool IsChild(int hWndParent, int hWnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int MapWindowPoints(int hWndFrom, int hWndTo, ref POINT point, int count);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int WindowFromPoint(POINT point);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int TrackMouseEvent(ref MouseTrackEvent trackEvent);
//獲取窗口標題
[DllImport("user32", SetLastError = true)]
public static extern int GetWindowText(
int hWnd, //窗口句柄
StringBuilder lpString, //標題
int nMaxCount //最大值
);
//獲取類的名字
[DllImport("user32.dll")]
private static extern int GetClassName(
int hWnd, //句柄
StringBuilder lpString, //類名
int nMaxCount //最大值
);
////根據坐標獲取窗口句柄
//[DllImport("user32")]
//private static extern IntPtr WindowFromPoint(
//POINT Point //坐標
//);
#endregion
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseTrackEvent
{
public uint cbSize;
public uint dwFlags;
public int hwndTrack;
public uint dwHoverTime;
}
}
注冊需要監聽的控件的事件,并對控件集合進行排序,根據包含關系由子到父進行排序
/// <summary>
/// 注冊一個監聽的控件。
/// </summary>
/// <param name="wrapper"></param>
public void Regist(RegistControlWrapper wrapper)
{
lock (m_lckObj)
{
m_RegistControls[wrapper.Handle] = wrapper;
//對集合進行包含性排序,即把子的排在前面
//對集合進行包含性排序,即把子的排在前面
m_KeyValuePairControls.Clear();
if (m_RegistControls.Count >= 2) //大于2就進行排序。
m_KeyValuePairControls = m_RegistControls.OrderBy(p => p, new ContainSubCtrlComparer<KeyValuePair<int, RegistControlWrapper>>()).ToList();
else if (m_RegistControls.Count == 1)
m_KeyValuePairControls = new List<KeyValuePair<int, RegistControlWrapper>>(m_RegistControls);
if (m_RegistControls.Count == 1)
{
Application.AddMessageFilter(this);
}
}
}
在處理鼠標事件中,最麻煩的是鼠標進入事件和離開事件,由于IMessageFilter對進程外的其他進程的消息是監聽不了的,而鼠標離開事件并不能根據WM_MOUSELEAVE 消息進行即時的處理,因此,必需更好的使用API才能實現。
使用GetCursorPos獲取當前光標的位置,然后通過WindowFromPoint獲取光標對應的控件的句柄,再由IsChild判斷句柄是否在訂閱的控件里來決定鼠標是否完全離開該控件,對于由于進入子控件而產生的WM_MOUSELEAVE 消息,還得在合適的時機調用TrackMouseEvent,以使該控件下次能再次發出WM_MOUSELEAVE 消息。
/// <summary>
/// 處理鼠標離開事件。
/// </summary>
/// <param name="m"></param>
/// <param name="isCancel"></param>
private void ProcessMouseLeave(ref Message m, ref bool cancel)
{
////如果是本集成的子控件導致頂層控件產生MouseLeave事件的,將屏閉該消息。
int handle = m.HWnd.ToInt32();
if (GetTopParenRegistControl(handle) > 0)
{
//獲取當前光標的位置下的控件是否為該控件的子
POINT pt = new POINT();
Win32API.GetCursorPos(ref pt); //屏幕
//獲取當前屏幕點下的控件
int newHandle = Win32API.WindowFromPoint(pt);
if (newHandle == m_CacheEntry.ParentHandle || Win32API.IsChild(m_CacheEntry.ParentHandle, newHandle))
{
if (m_CacheEntry.CurrentHandle == m_CacheEntry.ParentHandle)
{
cancel = true; //不做離開事件。如要下次再發出WM_MOUSELEAVE消息,得執行TrackMouseEvent方法(每調用些方法一次,才發出一次WM_MOUSELEAVE消息。)
m_CacheEntry.ControlWrapper.IsTrackLeave = false;
}
}
else
{
//真正的退出
//不做嵌套的控件的退出,覺得沒有意義
//SetLeaveFlags(ref cancel);
//RaiseAllMouseLeaveEvent(m_CacheEntry.CurrentHandle);
if (m_CacheEntry.CurrentHandle == m_CacheEntry.ParentHandle)
{
m_CacheEntry.ControlWrapper.IsTrackLeave = false;
}
else
RaiseMouseLeaveEvent(m_CacheEntry.ParentHandle); //做多處離去事件,由里往外, 不知進入的是否要多處的需求(由外往里)?
m_CacheEntry.CurrentHandle = 0;
m_CacheEntry.ParentHandle = 0;
}
}
}
鼠標離開的消息處理方法
在這個版本中,嚴謹度和清晰度大大提高,完全解決鼠標離開的消息,但調試中發現鼠標進入事件有時多執行一次,考慮到實現開發中多執行一次進入事件,影響不大,這個小Bug留真正有需要解決的讀者去完成,下面的完整的代碼,僅供學習。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:網絡轉載