您的位置:首页 > 其它

在.Net Compact Framework中为ListView添加Context Menu(Tap and hold)

2013-05-08 16:04 471 查看
话说,.Net Compact Framework比它的兄弟--完整版的Framework精简了不少,自然很多功能也没能提供了。然而对功能的需求是无止境的。这里我们要做的就是找寻那消失的属于ListView的Context Menu。
先说说遇到的问题吧。.Net Compact Framework中,ListView提供了Context Menu属性,但是设置之后实测,那个”点圈“倒是出现了,但是菜单没有。那么自然的想到在鼠标点击的事件中模拟,可惜这个事件没有(真没有)。自定义控件重写鼠标点击函数呢?人家不理你,根本不进来。

那怎么办呢?

1. 通过SetWindowLong,替换ListView的消息处理函数,并截获鼠标点击消息WM_LBUTTONDOWN

2. 在WM_LBUTTONDOWN消息中,使用SHRecognizeGesture模拟Tap and Hold

3. 显示Context Menu。

 

1. 替换ListView的消息处理函数,并截获鼠标点击消息WM_LBUTTONDOWN

    将替换消息处理函数并转发消息的功能封装为一个类Subclasser:

函数SubClass负责替换传入的hWindow的消息函数
函数
UnsubClass负责恢复hWindow的消息函数

函数
WndProc就是新的消息处理函数,如果Message有效,就转发出去。
      Subclasser主要代码如下:

public class Subclasser : IDisposable
{
        public delegate IntPtr WindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);

        public static WindowProc NewWindowDelegate;

        private static IntPtr OldWindowProc;

        private int GWL_WNDPROC = -4;

        [DllImport("coredll.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("coredll.dll")]
        static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("coredll.dll")]
        static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        private IntPtr m_windowHandle = (IntPtr)0;
 
        public event MessageEventHandler Message;
 
         public void SubClass(IntPtr hWindow)
        {
            if ((IntPtr)0 != hWindow)
            {
                m_windowHandle = hWindow;
                int newWndProc = Marshal.GetFunctionPointerForDelegate(NewWindowDelegate).ToInt32();
                int result = SetWindowLong(m_windowHandle, GWL_WNDPROC, newWndProc);
                OldWindowProc = (IntPtr) result;
            }
        }
 

         private void UnsubClass(IntPtr hWindow)

        {

            SetWindowLong(hWindow, GWL_WNDPROC, OldWindowProc.ToInt32());

        }

         public IntPtr WndProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam)

        {

            Message msg = new Message();

            msg.HWnd = hwnd;

            msg.Msg = (int)uMsg;

            msg.WParam = wParam;

            msg.LParam = lParam;

            MessageEventArgs msgEventArgs = new MessageEventArgs(msg);

            if (Message != null)

            {

                Message(this, msgEventArgs);

            }

            if (msgEventArgs.Result == (IntPtr)0)

            {

                return CallWindowProc(OldWindowProc, hwnd, uMsg, wParam, lParam);

            }

            return msgEventArgs.Result;

        }

2. 创建User Control,通过Subclasser接收并过滤出mouse down消息,实现Context Menu。
 
       2.1 创建User Control,仅包含ListView,封装并实现ListView的属性Columns,Items,View等等。
        public ListView.ColumnHeaderCollection Columns

        {

            get { return listView.Columns; }

        }

        public ListView.ListViewItemCollection  Items

        {

            get

            {

                return listView.Items;

            }

        }

        public View View

        {

            get { return listView.View; }

            set { listView.View = value; }

        }

 

2.2 通过Subclasser接收并过滤消息

 m_subClasser = new Subclasser();

         m_subClasser.Message += SubClasser_Message; 

 

 private void SubClasser_Message(object source, MessageEventArgs args)

        {

            if (args.Msg == WindowsMessages.WM_LBUTTONDOWN)

            {

                Byte[] point = BitConverter.GetBytes(args.LParam.ToInt32());

                Byte[] pointXByte = new byte[4];

                Byte[] pointYByte = new byte[4];

                Array.Copy(point,0,pointXByte,0,2);

                Array.Copy(point,2,pointYByte,0,2);

                int pointX = BitConverter.ToInt32(pointXByte,0);

                int pointY = BitConverter.ToInt32(pointYByte, 0);

                if (CheckContext(new MouseEventArgs(MouseButtons.Right, 1, pointX, pointY, 0), listView, listView.Handle))

                {

                    args.Result = (IntPtr) 1;

                }

                else

                {

                    args.Result = (IntPtr)0;

                }

            }

        }

 

2.3 模拟Tab and Hold

         internal struct SHRGINFO

        {

            public int cbSize;

            public IntPtr hwndClient;

            public int ptDownX;

            public int ptDownY;

            public SHRGFLags dwFlags;

        }

        [Flags]

        internal enum SHRGFLags

        {

            SHRG_RETURNCMD = 0x00000001,

            SHRG_NOTIFYPARENT = 0x00000002,

            SHRG_LONGDELAY = 0x00000008,

            SHRG_NOANIMATION = 0x00000010,

        }

        public const int GN_CONTEXTMENU = 1000;

        [DllImport("aygshell")]

        extern private static int SHRecognizeGesture(ref SHRGINFO shr);

        protected bool CheckContext(MouseEventArgs e, Control control, IntPtr hwnd)

        {

            if (RecognizeGesture(hwnd,e.X,e.Y))

            {

                ShowContext(control,e);

                return true;

            }

            return false;

        }

 

        protected virtual bool RecognizeGesture(IntPtr hwnd, int x, int y)

        {

            SHRGINFO info = new SHRGINFO();

            info.cbSize = Marshal.SizeOf(info);

            info.ptDownX = x;

            info.ptDownY = y;

            info.dwFlags = SHRGFLags.SHRG_RETURNCMD;

            info.hwndClient = hwnd;

            int cmd = SHRecognizeGesture(ref info);

            return (cmd == GN_CONTEXTMENU);

        }

 

        2.4 显示Context Menu

        protected void ShowContext(Control control, MouseEventArgs e)

        {

            if (ContextMenu != null)

            {

                ContextMenu.Show(control, new Point(e.X, e.Y));

            }

            if (null != TabHoldHandler)

            {

                TabHoldHandler(control,e);

            }

        }

 

3. 注意

一定要在控件做完初始化工作之后,再通过Subclasser替换掉默认的消息处理函数,否则有可能抛异常(原因目前未知)。或者说,在使用该控件的窗体的form loaded事件中进行替换比较好。

 

到这里,功能基本上都实现了,可能代码贴的有点多。希望不影响大家的理解。也不知道csdn怎么添加代码附件,:(

 

最后,说说没能解决的问题,由于想要封装,便于使用,于是做成了控件,但是不知道怎么实现design time support,也即无法在属性对话框中进行编辑。添加了该控件的窗体,即使通过代码在InitializeComponent函数中添加了该控件的行列,也会导致打开设计窗口时出错。求解。

 

参考文章:

http://msdn.microsoft.com/zh-cn/magazine/cc185722(en-us).aspx

http://msdn.microsoft.com/en-us/magazine/cc188736.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐