分享
 
 
 

关于三态的TreeView的一些想法

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

写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。。。还有。。。很多很多没做,这只是一种尝试。。过几天再来完善。。。

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有