写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<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) >> 0x10;
76
77IntPtr hitem = APIsUser32.SendMessage(this.Handle,APIsEnums.TreeViewMessages.HITTEST,0,ref hitTestInfo);
78
79if((hitTestInfo.flags & (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++)>