关于三态的TreeView的一些想法

写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
///

1<summary>   
2/// TreeView Messages / TVM   
3/// </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

  1<this.nodes.count;i++) !="CheckStates.Checked)" #endregion="" #region="" &="" ((extreenode)(this.nodes[i])).checked="CheckStates.Checked;" ((extreenode)(this.nodes[i])).checkeddirection="CheckDirection.Downwards;" :="" <summary="" base.checked="true;" break;="" case="" checkdirection.all)="" checkdirection.downwards)="CheckDirection.Downwards)" checkdirection.upwards)="CheckDirection.Upwards)" checkstates.checked:="" checkstates.halfchecked="" for(int="" halfchecked="" i="0;i&lt;this.Parent.Nodes.Count;i++)" if(((extreenode)this.parent.nodes[i]).checked="" if((this.checkeddirection="" if(this.parent="" parent="" return;="" this.parent.checked="CheckStates.Checked;" this.parent.checkeddirection="CheckDirection.Upwards;" {="" }="">   
  2/// Get the parent of this item   
  3///    
  4new public ExTreeNode Parent   
  5{   
  6get   
  7{   
  8return (ExTreeNode)base.Parent;   
  9}   
 10}   
 11#endregion   
 12#region TreeView   
 13public new ExTreeView TreeView   
 14{   
 15get   
 16{   
 17if(base.TreeView != null)return (ExTreeView)base.TreeView;   
 18if(Parent != null) return(Parent.TreeView);   
 19return(null);   
 20}   
 21}   
 22#endregion 
 23
 24加入了一个属性 
 25
 26#region CheckedDirection   
 27private CheckDirection checkDirection = CheckDirection.None;   
 28public CheckDirection CheckedDirection   
 29{   
 30get{return checkDirection;}   
 31set{this.checkDirection = value;}   
 32}   
 33#endregion 
 34
 35其中checkDirection和TreeListView的一样。 
 36
 37step5.开始写ExTreeView : TreeView了,添加了私有变量 
 38
 39private ExTreeNode _clickedNode = null; 
 40
 41重写了CheckBoxes属性。。 
 42
 43#region CheckBoxes   
 44private CheckBoxesTypes checkboxes = CheckBoxesTypes.None;   
 45[Category("Modified properties")]   
 46[DefaultValue(typeof(CheckBoxesTypes), "None")]   
 47[Browsable(true)]   
 48new public CheckBoxesTypes CheckBoxes   
 49{   
 50get   
 51{   
 52return checkboxes;   
 53}   
 54set   
 55{   
 56if(checkboxes == value) return;   
 57checkboxes = value;   
 58//checkDirection = value == CheckBoxesTypes.Recursive ? CheckDirection.All : CheckDirection.None;   
 59base.CheckBoxes = value == CheckBoxesTypes.None ? false : true;   
 60if(Created)   
 61Invalidate();   
 62}   
 63}   
 64#endregion 
 65
 66step6开始重写消息处理部分了 
 67
 68由于我准备只用click来重绘,而暂时不从CUSTOMDRAW里截取,所以代码如下 
 69
 70#region LBUTTONDOWN   
 71case APIsEnums.WindowMessages.LBUTTONDOWN: 
 72
 73APIsStructs.HITTESTINFO hitTestInfo = new APIsStructs.HITTESTINFO();   
 74hitTestInfo.pt.x = (short) ((int) m.LParam);   
 75hitTestInfo.pt.y = ((int) m.LParam) &gt;&gt; 0x10; 
 76
 77IntPtr hitem = APIsUser32.SendMessage(this.Handle,APIsEnums.TreeViewMessages.HITTEST,0,ref hitTestInfo); 
 78
 79if((hitTestInfo.flags &amp; (UInt32)APIsEnums.TVHTFLAGS.ONITEMSTATEICON) != 0 )   
 80{   
 81ExTreeNode checkedNode = (ExTreeNode)this.GetNodeAt(hitTestInfo.pt.x,hitTestInfo.pt.y);   
 82if(checkedNode == null || checkedNode.IsVisible == false)   
 83{   
 84this._clickedNode = null;   
 85m.Result = (IntPtr)1;   
 86return;   
 87}   
 88switch(checkedNode.Checked)   
 89{   
 90case CheckStates.UnChecked:   
 91checkedNode.CheckedDirection = CheckDirection.All;   
 92checkedNode.Checked = CheckStates.Checked;   
 93break;   
 94case CheckStates.HalfChecked:   
 95checkedNode.CheckedDirection = CheckDirection.All;   
 96checkedNode.Checked = CheckStates.UnChecked;   
 97this._clickedNode = checkedNode;   
 98m.Result = (IntPtr)1;   
 99return;   
100case CheckStates.Checked:   
101checkedNode.CheckedDirection = CheckDirection.All;   
102checkedNode.Checked = CheckStates.UnChecked;   
103break;   
104}   
105this._clickedNode = checkedNode;   
106}   
107break;   
108#endregion 
109
110然后重写OnClick及其对应的方法如下 
111
112protected override void OnClick(EventArgs e)   
113{   
114base.OnClick (e);   
115if(this._clickedNode != null)   
116{   
117ExTreeNode temp = this._clickedNode;   
118switch(this._clickedNode.Checked)   
119{   
120case CheckStates.UnChecked:   
121while(temp.Parent!=null)   
122{   
123if(temp.Parent.Checked == CheckStates.HalfChecked)   
124{   
125this.DrawHalfChecked(temp.Parent);   
126}   
127if(temp.Parent.Checked == CheckStates.Checked)   
128{   
129this.DrawChecked(temp.Parent);   
130}   
131temp = temp.Parent;   
132}   
133break;   
134case CheckStates.HalfChecked:   
135break;   
136case CheckStates.Checked:   
137while(temp.Parent!=null)   
138{   
139if(temp.Parent.Checked == CheckStates.HalfChecked)   
140{   
141this.DrawHalfChecked(temp.Parent);   
142}   
143if(temp.Parent.Checked == CheckStates.Checked)   
144{   
145this.DrawChecked(temp.Parent);   
146}   
147temp = temp.Parent;   
148}   
149break;   
150}   
151}   
152} 
153
154private void DrawHalfChecked(ExTreeNode node)   
155{   
156if(node.IsVisible)   
157{   
158Graphics g = Graphics.FromHwnd(this.Handle);   
159Rectangle recv = new Rectangle(node.Bounds.Location.X-11,node.Bounds.Location.Y+5,7,7);   
160Brush brush = new Drawing.Drawing2D.LinearGradientBrush(recv, Color.Gray, Color.LightBlue, 45, false);   
161g.FillRectangle(brush,recv);   
162}   
163} 
164
165private void DrawChecked(ExTreeNode node)   
166{   
167if(node.IsVisible)   
168{   
169Graphics g = Graphics.FromHwnd(this.Handle);   
170Rectangle recv = new Rectangle(node.Bounds.Location.X-11,node.Bounds.Location.Y+5,7,7);   
171Brush brush = new Drawing.Drawing2D.LinearGradientBrush(recv, Color.Brown, Color.Chocolate, 45, false);   
172g.FillRectangle(brush,recv);   
173}   
174} 
175
176由于我没有那个打勾的ICON,这里先用一个咖啡色的东西先表示那个勾,放在DrawChecked里。 
177
178到这里基本实现了外观的三态,其实更标准的是截获CUSTOMDRAW对每次重绘的item进行指定,但最近项目也忙,还没有那么多时间,等过段时间再来完成,实现真正的三态。。像那个ExTreeNode对应的editor都没有写,还有DrawHalfChecked里没有计算用image时的大小,如果用了还要再-16。。。还有。。。很多很多没做,这只是一种尝试。。过几天再来完善。。。 
179
180呵呵,谢谢各位看管,欢迎各位批评指正。。。</this.nodes.count;i++)>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus