本鱼拟成立工作室承接项目开发/软件定制/云设施开发运维/办公设备技术支持等,如您有相关需求,欢迎来询 | ::博客文章推荐::

修改ListView:如何让ListView在显示复选框时双击行不改变复选框的选中状态

: DOT.NET 木魚 2827℃ 0评论

.Net中自带的ListView,在显示复选框的情况下,默认设置当你双击行时会改变复选框的选中状态。一般情况下这个动作没有任何问题,但是有些情况下我们不需要,例如,我们定义了双击行显示编辑对话框,那这种情况下改变复选框的选中状态是很意外的。

那如何来禁止它呢?

 

首先查看MSDN类库,ListView的属性、函数,似乎没有可以设置这个操作的属性(至少我没找到)。

掏出Reflactor,反编译了System.Windows.Forms.dll,能看到这部分的核心代码如下:(位于 System.Windows.Forms.ListView 下)

   1:  private void WmNmDblClick(ref Message m)
   2:  {
   3:      if (this.CheckBoxes)
   4:      {
   5:          Point position = Cursor.Position;
   6:          position = base.PointToClientInternal(position);
   7:          NativeMethods.LVHITTESTINFO lParam = new NativeMethods.LVHITTESTINFO();
   8:          lParam.pt_x = position.X;
   9:          lParam.pt_y = position.Y;
  10:          int num = (int) UnsafeNativeMethods.SendMessage(new HandleRef(this, base.Handle), 0x1012, 0, lParam);
  11:          if ((num != -1) && ((lParam.flags & 14) != 0))
  12:          {
  13:              ListViewItem item = this.Items[num];
  14:              item.Checked = !item.Checked;
  15:          }
  16:      }
  17:  }
  18:   
  19:   

注意这个函数,是私有函数,并且不是虚函数,也就是说,我们想要通过重载来重写它,是行不通的。

这时候也许遇到这个问题的同学都会与我有过一样很挫的想法,就是在双击的处理事件中将活动行的Checkbox的选中状态给反过来?似乎可以(我没试验过),但是好像也太挫了吧。

那么就来扩展一下这个ListView吧。

创建一个新的ListViewExtend类,让它继承自原有的ListView,并加入一个属性:

   1:      public class ListViewExtend : System.Windows.Forms.ListView
   2:      {
   3:          public ListViewExtend()
   4:          {
   5:              EnableDblClickToCheckItem = true;
   6:          }
   7:   
   8:          #region 属性
   9:   
  10:          /// <summary>
  11:          /// 是否允许通过双击来选中复选框
  12:          /// </summary>
  13:          [Description("是否允许通过双击来选中复选框")]
  14:          public bool EnableDblClickToCheckItem { get; set; }
  15:   
  16:          #endregion
  17:   
  18:      }

经过查看源代码,上面提到的 WmNmDblClick 函数是在 WmReflectNotify 函数中调用的,而 WmReflectNotify 函数当然就是在 WndProc 中调用的了。

简单分析了一下WndProc和 WmReflectNotify 函数,写出了下面这个代码:

   1:      class NativeMethods
   2:      {
   3:          [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
   4:          public class LVHITTESTINFO
   5:          {
   6:              public int pt_x;
   7:              public int pt_y;
   8:              public int flags;
   9:              public int iItem;
  10:              public int iSubItem;
  11:          }
  12:   
  13:          [StructLayout(LayoutKind.Sequential)]
  14:          public struct NMHDR
  15:          {
  16:              public IntPtr hwndFrom;
  17:              public IntPtr idFrom;
  18:              public int code;
  19:          }
  20:      }
  21:   
  22:   
  23:          protected override void WndProc(ref System.Windows.Forms.Message m)
  24:          {
  25:              if (m.Msg == 0x204e && WmReflectNotify(ref m))
  26:              {
  27:                  //捕捉
  28:                  return;
  29:              }
  30:              else
  31:              {
  32:                  base.WndProc(ref m);
  33:              }
  34:          }
  35:   
  36:          unsafe bool WmReflectNotify(ref Message m)
  37:          {
  38:              NativeMethods.LVHITTESTINFO lvhittestinfo;
  39:              NativeMethods.NMHDR* lParam = (NativeMethods.NMHDR*)m.LParam;
  40:   
  41:              if (lParam->code == -3 && !EnableDblClickToCheckItem)
  42:              {
  43:                  return true;
  44:              }
  45:   
  46:              return false;
  47:          }
  48:   

思路很简单,当检测到是双击并且不允许这样的操作改变复选框状态时,直接过滤掉消息。

但是也许你会已经留意到问题了(很汗颜我是测试后才发现的),就是这样的话双击就会彻底无效了。本来想反编译一下剩下的代码看看如何执行除了改变复选框以外的其它的动作,但是无奈类中太多的方法都是private的,根本不允许改写和调用。索性直接搞上最简单的策略:直接触发事件。

在上面的43行,return之前加上一行:base.OnDoubleClick(new EventArgs()); ,搞定。

似乎不是那么优雅,哎。

喜欢 (0)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址