关于三态的TreeView的一些想法

news/2024/11/5 11:36:41 标签: null, properties, reference, 编译器, user, struct
<script type="text/javascript"> function StorePage() { d=document; t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():''); void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes')); keyit.focus(); } </script>

写TreeListView控件的那个人的确很牛X,看他的代码的确学到了不少东西。。。看后想自己也写个控件玩玩,看到有人要三态的TreeView,于是花了三天时间学习了一下TreeView控件,写了一些代码,初步达到效果。现将自己的方法介绍如下:

首先说说我用的资料:

编译器Reflector:看看MS的TreeView的架构和写法

MSDN的Tree-View Control Reference:了解每个message和结构的定义和用途

安装VS下的CommCtrl.h文件:了解message和一些枚举的实际值

做法如下:

step1.定义APIsEnums.cs文件,参照CommCtrl.h给出

#region TreeViewMessages / TVM
  /// <summary>
  /// TreeView Messages / TVM
  /// </summary>
  public enum TreeViewMessages : int
  {
   FIRST    = 0x1100,
   DELETEITEM   = FIRST+1,
   EXPAND    = FIRST+2,
   GETITEMRECT   = FIRST+4,
   GETCOUNT   = FIRST+5,
   GETINDENT   = FIRST+6,
   SETINDENT   = FIRST+7,

.....

}

在这里我用到的TV_Message其实只有HITTEST = FIRST+17,但为了学习,都列下来了。。。

step2.定义APIsStructs.cs文件,给出了

#region HITTESTINFO/TV
  [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=1)]
  public struct HITTESTINFO
  {
   public POINTAPI pt;
   public UInt32 flags;
   public IntPtr hItem;
  }
  #endregion

等结构,由于比较多,这里就不一一列举了。。但我主要用到的就这个,其他的比较常用的像什么NMHDR

的就不写出来了。。

step3 .定义APIsUser32.cs文件,给出了

//hittest
  [DllImport("user32.dll", CharSet=CharSet.Auto)]
  public static extern IntPtr SendMessage(IntPtr hWnd, APIsEnums.TreeViewMessages msg, int wParam, ref APIsStructs.HITTESTINFO lParam);
  //getRec
  [DllImport("user32.dll", CharSet=CharSet.Auto)]
  public static extern bool SendMessage(IntPtr hWnd, APIsEnums.TreeViewMessages msg, bool wParam, ref APIsStructs.RECT rc);

等。。。。

step4.开始写ExTreeNode:TreeNode了,在这里,我 修改了如下属性:

 #region Checked
   private CheckStates checkedState = CheckStates.UnChecked;
    [DefaultValue(typeof(CheckStates), "UnChecked")]
   public new CheckStates Checked
   {
    get
    {
     return checkedState;
    }
    set
    { 
     if(this.checkedState == value)
      return;
     else
     {
      CheckStates temp = this.checkedState;
      this.checkedState = value;
      switch(value)
      {
        #region UnChecked
       case CheckStates.UnChecked :
        if((this.CheckedDirection & CheckDirection.All) != CheckDirection.All)
        {
         base.Checked = false;
        }
        if((this.CheckedDirection & CheckDirection.Downwards) == CheckDirection.Downwards)
        {
         for(int i=0;i<this.Nodes.Count;i++)
         {
          ((ExTreeNode)(this.Nodes[i])).CheckedDirection = CheckDirection.Downwards;
          ((ExTreeNode)(this.Nodes[i])).Checked = CheckStates.UnChecked;
         }
        }
        if((this.CheckedDirection & CheckDirection.Upwards) == CheckDirection.Upwards)
        {
         if(this.Parent != null)
         {
          this.Parent.CheckedDirection = CheckDirection.Upwards;
          for(int i=0;i<this.Parent.Nodes.Count;i++)
          {
           if(((ExTreeNode)this.Parent.Nodes[i]).Checked != CheckStates.UnChecked)
           {
            this.Parent.Checked = CheckStates.HalfChecked;
            return;
           }
          }
          this.Parent.Checked = CheckStates.UnChecked;
         }
        }
        
        break;
        #endregion

        #region HalfChecked
       case CheckStates.HalfChecked :
        base.Checked = true;
        if((this.CheckedDirection & CheckDirection.Upwards) == CheckDirection.Upwards)
        {
         if(this.Parent != null)
         {
          this.Parent.CheckedDirection = CheckDirection.Upwards;
          this.Parent.Checked = CheckStates.HalfChecked;
         }
        }
        break;
        #endregion

       case CheckStates.Checked:
        if((this.CheckedDirection & CheckDirection.All) != CheckDirection.All)
        {
         base.Checked = true;
        }
        if((this.CheckedDirection & CheckDirection.Downwards) == CheckDirection.Downwards)
        {
         for(int i=0;i<this.Nodes.Count;i++)
         {
          ((ExTreeNode)(this.Nodes[i])).CheckedDirection = CheckDirection.Downwards;
          ((ExTreeNode)(this.Nodes[i])).Checked = CheckStates.Checked;
         }
        }
        
        if((this.CheckedDirection & CheckDirection.Upwards) == CheckDirection.Upwards)
        {
         if(this.Parent != null)
         {
          this.Parent.CheckedDirection = CheckDirection.Upwards;
          for(int i=0;i<this.Parent.Nodes.Count;i++)
          {
           if(((ExTreeNode)this.Parent.Nodes[i]).Checked != CheckStates.Checked)
           {
            this.Parent.Checked = CheckStates.HalfChecked;
            return;
           }
          }
          this.Parent.Checked = CheckStates.Checked;
         }
        }
        break;
      }
     }

    }
   }
   #endregion
   #region Parent
   /// <summary>
   /// Get the parent of this item
   /// </summary>
   new public ExTreeNode Parent
   {
    get
    {
     return (ExTreeNode)base.Parent;
    }
   }
   #endregion
   #region TreeView
   public new ExTreeView TreeView
   {
    get
    {
     if(base.TreeView != null)return (ExTreeView)base.TreeView;
     if(Parent != null) return(Parent.TreeView);
     return(null);
    }
   }
   #endregion

加入了一个属性

#region CheckedDirection
   private CheckDirection checkDirection = CheckDirection.None;
   public CheckDirection CheckedDirection
   {
    get{return checkDirection;}
    set{this.checkDirection = value;}
   }
   #endregion

其中checkDirection和TreeListView的一样。

step5.开始写ExTreeView : TreeView了,添加了私有变量

private ExTreeNode _clickedNode = null;

重写了CheckBoxes属性。。

#region CheckBoxes
   private CheckBoxesTypes checkboxes = CheckBoxesTypes.None;
   [Category("Modified properties")]
   [DefaultValue(typeof(CheckBoxesTypes), "None")]
   [Browsable(true)]
   new public CheckBoxesTypes CheckBoxes
   {
    get
    {
     return checkboxes;
    }
    set
    {
     if(checkboxes == value) return;
     checkboxes = value;
     //checkDirection = value == CheckBoxesTypes.Recursive ? CheckDirection.All : CheckDirection.None;
     base.CheckBoxes = value == CheckBoxesTypes.None ? false : true;
     if(Created)
      Invalidate();
    } 
   }
   #endregion

step6开始重写消息处理部分了

由于我准备只用click来重绘,而暂时不从CUSTOMDRAW里截取,所以代码如下

#region  LBUTTONDOWN  
    case APIsEnums.WindowMessages.LBUTTONDOWN:

     APIsStructs.HITTESTINFO hitTestInfo = new APIsStructs.HITTESTINFO();
     hitTestInfo.pt.x = (short) ((int) m.LParam);
     hitTestInfo.pt.y = ((int) m.LParam) >> 0x10;

     IntPtr hitem = APIsUser32.SendMessage(this.Handle,APIsEnums.TreeViewMessages.HITTEST,0,ref hitTestInfo);

     if((hitTestInfo.flags & (UInt32)APIsEnums.TVHTFLAGS.ONITEMSTATEICON) != 0 )
     {
      ExTreeNode checkedNode = (ExTreeNode)this.GetNodeAt(hitTestInfo.pt.x,hitTestInfo.pt.y);
      if(checkedNode == null || checkedNode.IsVisible == false)
      {
       this._clickedNode = null;
       m.Result = (IntPtr)1;
       return;
      }     
      switch(checkedNode.Checked)
      {
       case CheckStates.UnChecked:
        checkedNode.CheckedDirection = CheckDirection.All;
        checkedNode.Checked = CheckStates.Checked;
        break;
       case CheckStates.HalfChecked:
        checkedNode.CheckedDirection = CheckDirection.All;
        checkedNode.Checked = CheckStates.UnChecked;
        this._clickedNode = checkedNode;
        m.Result = (IntPtr)1;
        return;
       case CheckStates.Checked:
        checkedNode.CheckedDirection = CheckDirection.All;
        checkedNode.Checked = CheckStates.UnChecked;
        break;
      }
      this._clickedNode = checkedNode;
     }
     break;
    #endregion

然后重写OnClick及其对应的方法如下

protected override void OnClick(EventArgs e)
  {
   base.OnClick (e);
   if(this._clickedNode != null)
   {
    ExTreeNode temp = this._clickedNode;
    switch(this._clickedNode.Checked)
    {
     case CheckStates.UnChecked:
      while(temp.Parent!=null)
      {
       if(temp.Parent.Checked == CheckStates.HalfChecked)
       {
        this.DrawHalfChecked(temp.Parent);
       }
       if(temp.Parent.Checked == CheckStates.Checked)
       {
        this.DrawChecked(temp.Parent);
       }
       temp = temp.Parent;
      }
      break;
     case CheckStates.HalfChecked:
      break;
     case CheckStates.Checked:
      while(temp.Parent!=null)
      {
       if(temp.Parent.Checked == CheckStates.HalfChecked)
       {
        this.DrawHalfChecked(temp.Parent);
       }
       if(temp.Parent.Checked == CheckStates.Checked)
       {
        this.DrawChecked(temp.Parent);
       }
       temp = temp.Parent;
      }
      break;
    }
   }
  }

private void DrawHalfChecked(ExTreeNode node)
  {
   if(node.IsVisible)
   {
    Graphics g = Graphics.FromHwnd(this.Handle);
    Rectangle recv = new Rectangle(node.Bounds.Location.X-11,node.Bounds.Location.Y+5,7,7);
    Brush brush = new Drawing.Drawing2D.LinearGradientBrush(recv, Color.Gray, Color.LightBlue, 45, false);
    g.FillRectangle(brush,recv);
   }
  }

  private void DrawChecked(ExTreeNode node)
  {
   if(node.IsVisible)
   {
    Graphics g = Graphics.FromHwnd(this.Handle);
    Rectangle recv = new Rectangle(node.Bounds.Location.X-11,node.Bounds.Location.Y+5,7,7);
    Brush brush = new Drawing.Drawing2D.LinearGradientBrush(recv, Color.Brown, Color.Chocolate, 45, false);
    g.FillRectangle(brush,recv);
   }
  }

由于我没有那个打勾的ICON,这里先用一个咖啡色的东西先表示那个勾,放在DrawChecked里。

到这里基本实现了外观的三态,其实更标准的是截获CUSTOMDRAW对每次重绘的item进行指定,但最近项目也忙,还没有那么多时间,等过段时间再来完成,实现真正的三态。。像那个ExTreeNode对应的editor都没有写,还有DrawHalfChecked里没有计算用image时的大小,如果用了还要再-16。。。还有。。。很多很多没做,这只是一种尝试。。过几天再来完善。。。

呵呵,谢谢各位看管,欢迎各位批评指正。。。


http://www.niftyadmin.cn/n/927215.html

相关文章

在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分

///*标题&#xff1a;在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分当看到.NET中TcpListener和TcpClient的时候,我非常高兴,那就是我想要的通讯模式但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们.下面提供了一些类,可以…

用Visual C#打造个性化的IE浏览器IE

是现在Windows平台下用的最多的浏览器&#xff0c;但微软提供的IE是那么朴实&#xff0c;以至于毫无特色&#xff0c;那么如何用程序来修改IE,打造有自己的特色的IE呢&#xff1f;我经过思索&#xff0c;通过注册表找到了修改IE的方法&#xff0c;下面我向大家介绍一下这种方法…

蛙蛙推荐:一套.net窗体身份验证方案(解决了防止用户重复登陆,session超时等问题)

<script type"text/javascript"> function StorePage() { ddocument; td.selection?(d.selection.type!None?d.selection.createRange().text:):(d.getSelection?d.getSelection():); void(keyitwindow.open(http://www.365key.com/storeit.aspx?tescape(d…

母亲 (转)

我不相信我看到的情景&#xff0c;妈妈躺在26号病床上&#xff0c;口里插着人工的心脏起驳器。身上 到处贴着心电图探针&#xff0c;双眼紧闭。四周有一群护士医生&#xff0c;和住得较近的亲戚们。哥哥哭 着对我说&#xff1a;“妈妈不行了&#xff0c;救不过来呀…”我看着仪…

linux 退出服务器_Linux 上搭建 FTP 服务

参考网址&#xff1a;云服务器 Linux 云服务器搭建 FTP 服务​cloud.tencent.com操作场景Vsftpd&#xff08;very secure FTP daemon&#xff09;是众多 Linux 发行版中默认的 FTP 服务器。本文以 CentOS 7.6 64位操作系统的腾讯云服务器&#xff08;CVM&#xff09;为例&#…

世界第一台电脑_师从美利坚--苏联UCS电脑

之前我们聊过了苏联的明斯克系列计算机。苏联的计算机有着自己独特的思路和技术标准。在这一时期&#xff0c;苏联有着完整的计算机产业链&#xff0c;整体技术实力仅次于美国。苏联在计算机上虽然小有所成&#xff0c;最终因不敌美国计算机而被迫放弃。之后苏联领导人决心师从…

电脑剪贴板在哪里打开_这个小玩意让手机的推送无缝显示在电脑上_办公软件...

2020-10-20 19:30:004点赞33收藏6评论9月30日-10月30日&#xff0c;参与#小米智能生活#征稿活动&#xff0c;聊一聊你的米家好物选购攻略&#xff0c;五千元巨额奖金等你来拿&#xff01;点击查看活动详情。关于手机连接电脑的第三方工具已经给大家介绍过不少了&#xff0c;算是…

python 10个100以内随机整数编辑_Python语法整理

刹客网络科技资讯点击右侧关注&#xff0c;最新科技资讯&#xff01;了解 python 1. 了解 Python Python 是一种解释型(这意味着开发过程中没有了编译这个环节)、面向对象(支持面向对象的风格或代码封装在对象的编程技术)、动态数据类型的交互式(可在命令行中通过Python 提示…