using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing.Design;
namespace WindowsFormsApplication4
{
/// <summary>
/// 三态树控件类,此类继承于TreeViewDrag
/// </summary>
[Serializable()]
public class ThreeStateIconTreeView
: System.Windows.Forms.TreeView
{
#region "构造函数与析构函数"
/// <summary>
/// 初始化 ThreeStateIconTreeView 类的新实例。
/// </summary>
public ThreeStateIconTreeView()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ThreeStateIconTreeView));
this.imgCheckBoxState = new System.Windows.Forms.ImageList(this.components);
this.imgRadioBoxState = new System.Windows.Forms.ImageList(this.components);
this.SuspendLayout();
//
// imgCheckBoxState
//
this.imgCheckBoxState.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imgCheckBoxState.ImageStream")));
this.imgCheckBoxState.TransparentColor = System.Drawing.Color.Transparent;
this.imgCheckBoxState.Images.SetKeyName(0, "01.ico");
this.imgCheckBoxState.Images.SetKeyName(1, "02.ico");
this.imgCheckBoxState.Images.SetKeyName(2, "03.ico");
//
// imgRadioBoxState
//
this.imgRadioBoxState.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
this.imgRadioBoxState.ImageSize = new System.Drawing.Size(13, 13);
this.imgRadioBoxState.TransparentColor = System.Drawing.Color.Transparent;
this.ResumeLayout(false);
}
/// <summary>
/// 初始化 ThreeStateIconTreeView 类的新实例。
/// </summary>
public ThreeStateIconTreeView(System.ComponentModel.IContainer container)
: this()
{
container.Add(this);
InitializeComponent();
}
/// <summary>
/// 释放资源。
/// </summary>
~ThreeStateIconTreeView()
{
this.Dispose();
}
#endregion
#region "内部变量"
private bool m_bReadOnly;
private bool m_bOnlyLeavesCheckBox;
private bool m_bThreeState = true;
//private ImageList m_imgListState=null;
private EnumTreeViewType m_eTreeViewType = EnumTreeViewType.多选三态树;
private bool m_bRadioCheck;
private EnumRadioCheckType m_eRadioCheckType = EnumRadioCheckType.TreeView;
private TreeNode m_nodeRadioCheckedParent; //当m_eRadioCheckType=EnumRadioCheckType.TreeView,记录被选中的项
private bool m_bSelectNodeAtRButtonMouseDownOneNode = true;
internal System.Windows.Forms.ImageList imgCheckBoxState;
internal System.Windows.Forms.ImageList imgRadioBoxState;
private System.ComponentModel.IContainer components;
#endregion
#region "属性"
/// <summary>
/// 只读属性,如果为True,则用户不能在界面上修改节点的CheckBox状态,<br></br>
/// 如果为False,则用户可以通过鼠标或键盘修改节点的CheckBox状态。
/// </summary>
//***********************************************************
//* 属性名称:readonly
//* 功能描述:只读属性
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
[Category("自定义属性")]
[Description("只读属性"), DefaultValue(false)]
public bool ReadOnly
{
get
{
return m_bReadOnly;
}
set
{
m_bReadOnly = value;
}
}
/// <summary>
/// 只有叶子打勾属性,如果为True,则只有叶子节点又CheckBox,其他节点没有<br></br>
/// 如果为False,则所有节点都有CheckBox。
/// </summary>
//***********************************************************
//* 属性名称:OnlyLeavesCheckBox
//* 功能描述:只有叶子打勾属性
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
[Category("自定义属性")]
[Description("决定是否只有叶子有CheckBox"), DefaultValue(false)]
public bool OnlyLeavesCheckBox
{
get { return m_bOnlyLeavesCheckBox; }
set
{
if (m_bOnlyLeavesCheckBox == value)
return;
m_bOnlyLeavesCheckBox = value;
Refresh();
}
}
/// <summary>
/// 决定节点有无CheckBox。
/// </summary>
//***********************************************************
//* 属性名称:CheckBoxes
//* 功能描述:重写CheckBoxes属性
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public new bool CheckBoxes
{
get
{
return base.CheckBoxes;
}
set
{
base.CheckBoxes = value;
Refresh();
}
}
/// <summary>
/// 节点的图像列表。
/// </summary>
//***********************************************************
//* 属性名称:ImageList
//* 功能描述:重写ImageList属性
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public new ImageList ImageList
{
get
{
return base.ImageList;
}
set
{
base.ImageList = value;
Refresh();
}
}
/// <summary>
/// 树类型。
/// </summary>
[Browsable(true)]
[Category("自定义属性")]
[Description("下拉框显示的树控件类型")]
[DefaultValue(EnumTreeViewType.标准树)]
public EnumTreeViewType TreeViewType
{
get { return m_eTreeViewType; }
set
{
if (m_eTreeViewType == value)
return;
m_eTreeViewType = value;
switch (m_eTreeViewType)
{
case EnumTreeViewType.标准树:
base.CheckBoxes = false;
break;
case EnumTreeViewType.多选一般树:
base.CheckBoxes = true;
m_bThreeState = false;
m_bRadioCheck = false;
break;
case EnumTreeViewType.多选三态树:
base.CheckBoxes = true;
m_bThreeState = true;
m_bRadioCheck = false;
break;
case EnumTreeViewType.单选一般树:
base.CheckBoxes = true;
m_bRadioCheck = true;
m_eRadioCheckType = EnumRadioCheckType.TreeView;
break;
case EnumTreeViewType.单选同一父节点树:
base.CheckBoxes = true;
m_bRadioCheck = true;
m_eRadioCheckType = EnumRadioCheckType.TreeNode;
break;
}
Refresh();
}
}
/// <summary>
/// 鼠标右键单击节点时,是否立即选中该节点。
/// </summary>
//***********************************************************
//* 属性名称:SelectNodeAtRButtonMouseDownOneNode
//* 功能描述:鼠标右键单击节点时,是否立即选中该节点
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
[Category("自定义属性")]
[DefaultValue(true)]
[Description("决定鼠标右键MouseDown一个节点时,是否选中该节点!")]
public bool SelectNodeAtRButtonMouseDownOneNode
{
get { return m_bSelectNodeAtRButtonMouseDownOneNode; }
set { m_bSelectNodeAtRButtonMouseDownOneNode = value; }
}
/// <summary>
/// 是否是三态树属性,如果True,则节点CheckBox状态改变,其父节点和子节点状态会相应改变,<br></br>
/// 如果false,则节点CheckBox状态改变,其父节点和子节点状态不会改变!
/// </summary>
//***********************************************************
//* 属性名称:ThreeState
//* 功能描述:是否三态树属性
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
[Category("自定义属性")]
[DefaultValue(true)]
[Description("是否是三态树属性,如果false,则节点CheckBox状态改变,其父节点和子节点状态不会改变!")]
internal bool ThreeState
{
get { return m_bThreeState; }
set { m_bThreeState = value; }
}
//***********************************************************
//* 属性名称:RadioCheck
//* 功能描述:决定是单选还是多选。
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
[Category("自定义属性")]
[Description("只能单选属性"), DefaultValue(false)]
internal bool RadioCheck
{
get
{
return m_bRadioCheck;
}
set
{
if (m_bRadioCheck == value)
return;
m_bRadioCheck = value;
Refresh();
}
}
#endregion
#region "Win-API 相关的结构和方法"
[StructLayout(LayoutKind.Sequential)]
private struct TVITEM
{
public int mask;
public IntPtr hItem;
public int state;
public int stateMask;
public int pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public int lParam;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TV_HITTESTINFO
{
public Point pt; //点结构;
public TVHit flags; //标志;
public IntPtr hItem;
} ;
[Flags]
internal enum TVHit
{
NoWhere = 0x1,
OnItemIcon = 0x2,
OnItemLabel = 0x4,
OnItemIndent = 0x8,
OnItemButton = 0x10,
OnItemRight = 0x20,
OnItemStateIcon = 0x40,
OnItem = (OnItemIcon | OnItemLabel | OnItemStateIcon),
Above = 0x100,
Below = 0x200,
ToRight = 0x400,
ToLeft = 0x800,
TVIS_STATEIMAGEMASK = 0xF000
};
internal enum TreeViewMessages //消息
{
TV_FIRST = 0x1100,
TVM_SETIMAGELIST = TV_FIRST + 9,
TVM_GETITEM = TV_FIRST + 12,
TVM_SETITEM = TV_FIRST + 13,
TVM_HITTEST = TV_FIRST + 17
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, ref TV_HITTESTINFO lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, ref TVITEM lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, IntPtr lParam);
#endregion
#region "内部方法"
//***********************************************************
//* 函数名称:CheckNode
//* 功能描述:设置节点状态
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
private void CheckNode(TreeNode node, EnumCheckState state)
{
BeginUpdate();
SetInternalChecked(node, state);
if (m_bThreeState && !m_bRadioCheck)
{
foreach (TreeNode child in node.Nodes)
{
CheckNode(child, state);
}
}//
if (m_bRadioCheck && state == EnumCheckState.Checked)
{
if (m_eRadioCheckType == EnumRadioCheckType.TreeView)
{
SetChildNodeUnChecked(m_nodeRadioCheckedParent, node);
m_nodeRadioCheckedParent = node.Parent;
}
else
{
SetChildNodeUnChecked(node.Parent, node);
}
}
EndUpdate();
}
//***********************************************************
//* 函数名称:ChangeParent
//* 功能描述:改变父节点状态
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
private void ChangeParent(TreeNode node)
{
if (node == null)
{
return;
}
if (GetChecked(node) == EnumCheckState.None)
{
return;
}
EnumCheckState state = GetChecked(node.FirstNode);
foreach (TreeNode child in node.Nodes)
{
if (GetChecked(child) == EnumCheckState.Indeterminate || GetChecked(child) != state)
{
state = EnumCheckState.Indeterminate;
break;
}
} //
SetInternalChecked(node, state);
ChangeParent(node.Parent);
}
//***********************************************************
//* 函数名称:ChangeNodeState
//* 功能描述:改变节点状态
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
private void ChangeNodeState(TreeNode node)
{
EnumCheckState state = GetChecked(node);
BeginUpdate();
switch (state)
{
case EnumCheckState.Checked:
CheckNode(node, EnumCheckState.Unchecked);
break;
case EnumCheckState.Indeterminate:
case EnumCheckState.Unchecked:
CheckNode(node, EnumCheckState.Checked);
break;
}
if (m_bThreeState && !m_bRadioCheck)
{
ChangeParent(node.Parent);
}
EndUpdate();
}
private TreeNode GetTreeNodeHitAtCheckBoxByClientPosition(int x, int y)
{
TreeNode node = null;
int iTreeNodeHandle;
TV_HITTESTINFO tTVHITTESTINFO = new TV_HITTESTINFO();
tTVHITTESTINFO.pt.X = x;
tTVHITTESTINFO.pt.Y = y;
iTreeNodeHandle = SendMessage(this.Handle, TreeViewMessages.TVM_HITTEST, 0, ref tTVHITTESTINFO);
if (iTreeNodeHandle != 0)
{
if ((tTVHITTESTINFO.flags & TVHit.OnItemStateIcon) != 0)
{
node = TreeNode.FromHandle(this, new IntPtr(iTreeNodeHandle));
}
}
return node;
}
private void SetParentCheckBoxStateNone(TreeNodeCollection Nodes)
{
foreach (TreeNode node in Nodes)
{
if (node.Nodes.Count > 0)
{
SetInternalChecked(node, EnumCheckState.None);
SetParentCheckBoxStateNone(node.Nodes);
}
}
}
private EnumCheckState SetParentCheckBoxStateNotNone(TreeNode Node)
{
EnumCheckState stateRet = EnumCheckState.None;
if (Node.Nodes[0].Nodes.Count > 0)
{
stateRet = SetParentCheckBoxStateNotNone(Node.Nodes[0]);
}
else
{
stateRet = GetChecked(Node.Nodes[0]);
}
for (int i = 1; i < Node.Nodes.Count; i++)
{
EnumCheckState state;
if (Node.Nodes[i].Nodes.Count > 0)
{
state = SetParentCheckBoxStateNotNone(Node.Nodes[i]);
}
else
{
state = GetChecked(Node.Nodes[i]);
}
if (stateRet != state)
{
stateRet = EnumCheckState.Indeterminate;
break;
}
}
SetInternalChecked(Node, stateRet);
return stateRet;
}
private void SetChildNodeUnChecked(TreeNode node, TreeNode butThisNodeUnChanged)
{
TreeNodeCollection Nodes;
if (node == null)
Nodes = this.Nodes;
else
Nodes = node.Nodes;
foreach (TreeNode nod in Nodes)
{
if (nod == butThisNodeUnChanged)
continue;
if (GetChecked(nod) == EnumCheckState.Checked)
{
SetInternalChecked(nod, EnumCheckState.Unchecked);
return;
}
}
}
#endregion
#region "外部方法"
/// <summary>
/// 设置状态图标列表。
/// </summary>
/// <param name="ImgListState">
/// 状态图标列表
/// </param>
//***********************************************************
//* 函数名称:SetStateImageList
//* 功能描述:设置状态图标的ImageList
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public void SetStateImageList(ImageList ImgListState)
{
if (this.CheckBoxes == true)
{
if (ImgListState != null)
{
SendMessage(this.Handle, TreeViewMessages.TVM_SETIMAGELIST, 2, ImgListState.Handle);
}
else
{
//SendMessage(this.Handle,TreeViewMessages.TVM_SETIMAGELIST,2,IntPtr.Zero);
if (!m_bRadioCheck)
{
SendMessage(this.Handle, TreeViewMessages.TVM_SETIMAGELIST, 2, this.imgCheckBoxState.Handle);
}
else
{
SendMessage(this.Handle, TreeViewMessages.TVM_SETIMAGELIST, 2, this.imgRadioBoxState.Handle);
}
}
}
}
/// <summary>
/// 获取某个节点的状态。
/// </summary>
/// <param name="node">要获取状态的节点</param>
/// <returns>当前节点的状态</returns>
//***********************************************************
//* 函数名称:GetChecked
//* 功能描述:获取当前节点的状态
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public EnumCheckState GetChecked(TreeNode node)
{
TVITEM tTVITEM = new TVITEM();
int iState;
int iResult;
tTVITEM.mask = (int)(TVHit.OnItemButton | TVHit.OnItemIndent);
tTVITEM.hItem = node.Handle;
tTVITEM.stateMask = (int)(TVHit.TVIS_STATEIMAGEMASK);
tTVITEM.state = 0;
iResult = SendMessage(this.Handle, TreeViewMessages.TVM_GETITEM, 0, ref tTVITEM);
if (iResult != 0)
{
iState = tTVITEM.state;
iState = iState / 0xFFF;
return (EnumCheckState)(iState);
}
else
{
return EnumCheckState.Unchecked;
}
}
/// <summary>
/// 设置节点状态。
/// </summary>
/// <param name="node">要设置的节点</param>
/// <param name="state">设置的状态</param>
//***********************************************************
//* 函数名称:SetChecked
//* 功能描述:设置节点的状态
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public void SetChecked(TreeNode node, EnumCheckState state)
{
CheckNode(node, state);
if (m_bThreeState && !m_bRadioCheck)
{
ChangeParent(node.Parent);
}
}
/// <summary>
/// 设置一个节点的状态,且当前节点的父节点和子节点状态不改变。
/// </summary>
/// <param name="node">要设置的节点</param>
/// <param name="state">设置的状态</param>
//***********************************************************
//* 函数名称:SetInternalChecked
//* 功能描述:改变当前节点的状态
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public void SetInternalChecked(TreeNode node, EnumCheckState state)
{
int iState = (int)state;
TVITEM tTVITEM = new TVITEM();
tTVITEM.mask = (int)(TVHit.OnItemButton | TVHit.OnItemIndent);
tTVITEM.hItem = node.Handle;
tTVITEM.stateMask = (int)TVHit.TVIS_STATEIMAGEMASK;
tTVITEM.state = iState * 0x1000;
SendMessage(this.Handle, TreeViewMessages.TVM_SETITEM, 0, ref tTVITEM);
base.OnAfterCheck(new System.Windows.Forms.TreeViewEventArgs(node));
}
#endregion
#region "重写方法"
/// <summary>
/// 重写控件的 Refresh 方法,重绘控件
/// </summary>
//***********************************************************
//* 函数名称:Refresh
//* 功能描述:重写Refresh方法
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
public override void Refresh()
{
SetStateImageList(null);
if (m_bOnlyLeavesCheckBox && this.Nodes.Count > 0)
{
SetParentCheckBoxStateNone(this.Nodes);
}
else
{
// foreach(TreeNode node in this.Nodes)
// {
// if(node.Nodes.Count>0)
// SetParentCheckBoxStateNotNone(node);
// }
}
base.Refresh();
}
/// <summary>
/// 重写控件的 OnParentChanged 方法,重绘控件
/// </summary>
//***********************************************************
//* 函数名称:OnParentChanged
//* 功能描述:重写OnParentChanged方法
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
protected override void OnParentChanged(EventArgs e)
{
Refresh();
base.OnParentChanged(e);
}
/// <summary>
/// 重写控件的 OnClick 方法,判断鼠标位置是否在状态图标内,如果在,则改变节点状态。
/// </summary>
//***********************************************************
//* 函数名称:OnClick
//* 功能描述:重写单击事件,判断鼠标单击位置
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
protected override void OnClick(System.EventArgs e)
{
Point pt = PointToClient(Control.MousePosition);
base.OnClick(e);
if (m_bReadOnly || m_bCancel)
{
return;
}
TreeNode node = GetTreeNodeHitAtCheckBoxByClientPosition(pt.X, pt.Y);
if (node != null)
{
ChangeNodeState(node);
}
}
/// <summary>
/// 重写控件的 OnVisibleChanged 方法,重绘控件
/// </summary>
protected override void OnVisibleChanged(EventArgs e)
{
if (this.Visible)
Refresh();
base.OnVisibleChanged(e);
}
/// <summary>
/// 重写控件的 OnMouseDown 方法,鼠标右键选中节点
/// </summary>
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right && m_bSelectNodeAtRButtonMouseDownOneNode)
{
TreeNode node = this.GetNodeAt(e.X, e.Y);
if (node != null)
this.SelectedNode = node;
}
base.OnMouseDown(e);
}
/// <summary>
/// 重写控件的 OnKeyDown 方法,当用户按下空格键,改变节点状态。
/// </summary>
//***********************************************************
//* 函数名称:OnKeyDown
//* 功能描述:重写KeyDown事件,判断按下的是否是空格键
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.Space)
{
if (m_bReadOnly || m_bCancel)
{
return;
}
if (SelectedNode != null)
{
ChangeNodeState(SelectedNode);
}
}
}
/// <summary>
/// 重写控件的 OnBeforeCheck 方法,取消Check。
/// </summary>
//***********************************************************
//* 函数名称:OnBeforeCheck
//* 功能描述:重写OnBeforeCheck事件,取消Check
//* 参 数:无
//* 返 回 值:
//* 作 者:
//* 创建日期:2004-06-28
//***********************************************************
protected override void OnBeforeCheck(System.Windows.Forms.TreeViewCancelEventArgs e)
{
if (m_bReadOnly == true)
{
e.Cancel = true;
m_bCancel = e.Cancel;
return;
}
//>>防止单选按钮取消
if (m_bRadioCheck && GetChecked(e.Node) == EnumCheckState.Checked)
{
e.Cancel = true;
m_bCancel = e.Cancel;
return;
}
//<<防止单选按钮取消
base.OnBeforeCheck(e);
if (e.Cancel == false)
ChangeNodeState(e.Node);
m_bCancel = e.Cancel;
}
private bool m_bCancel = false;
#endregion
}
#region "EnumTreeViewType --- 树类型"
/// <summary>
/// 树类型枚举。
/// </summary>
public enum EnumTreeViewType
{
/// <summary>
/// 标准树,当前树和<see cref="System.Windows.Forms.TreeView">System.Windows.Forms.TreeView</see> 类似
/// </summary>
标准树 = 0,
/// <summary>
/// 多选一般树,可以多选,但不支持三态
/// </summary>
多选一般树 = 1,
/// <summary>
/// 多选三态树
/// </summary>
多选三态树 = 2,
/// <summary>
/// 单选一般树,整个树最多只能选中一个节点
/// </summary>
单选一般树 = 3,
/// <summary>
/// 单选同一父节点树,整个树可以选中多个节点,但同一父节点下,最多只能选中一个子节点
/// </summary>
单选同一父节点树 = 4
}
#endregion
#region "EnumCheckState --- CheckBox状态"
/// <summary>
/// 节点状态类型枚举
/// </summary>
public enum EnumCheckState
{
/// <summary>
/// 没有复选框
/// </summary>
None = 0, //没有复选框
/// <summary>
/// 复选框为不选中状态
/// </summary>
Unchecked = 1, //复选框为没有选中状态
/// <summary>
/// 复选框为选中状态
/// </summary>
Checked = 2, //复选框为选中状态
/// <summary>
/// 复选框为中间状态
/// </summary>
Indeterminate = 3 //复选框为半选中状态
}
#endregion
#region "EnumRadioCheckType --- RadioCheck类型"
/// <summary>
/// 单选树的单选类型
/// </summary>
public enum EnumRadioCheckType
{
/// <summary>
/// 整个树最多只能有一个节点被选中
/// </summary>
TreeView = 0,
/// <summary>
/// 整个树可以有多个节点被选中,但同一父节点下,最多只能有一个子节点被选中
/// </summary>
TreeNode = 1
}
#endregion
}