当前位置: 代码迷 >> 综合 >> 在.Net Compact Framework中为ListView添加Context Menu(Tap and hold)
  详细解决方案

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

热度:47   发布时间:2024-01-06 05:53:27.0

话说,.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

  相关解决方案