//==============================================================
//  版权所有：深圳杰文科技
//  文件名：TreeListCheckBox.cs
//  版本：V1.0  
//  创建者：Jay  ( QQ： 85363208 )
//  创建时间：2017-11-28 16:18
//  创建描述：
//  修改者：
//  修改时间：
//  修改说明： 
//==============================================================

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Columns;
using DevExpress.XtraTreeList.Nodes;

namespace Infrastructure.Components
{
	[ToolboxItem(false)]
	public class TreeListCheckBox : TreeList
	{
		#region NodeState

		public enum NodeState //节点的选中样式
		{
			Unchecked = 0, //未选中
			Checked = 1, //已选中
			Mixed = 2 //混合选中
		}

		private class TagValue
		{
			public object OriginValue = null;
			public object CheckedValue = null;

			public TagValue(object orginValue)
			{
				this.OriginValue = orginValue;
			}
		}

		#endregion

		public TreeListCheckBox()
		{
			Init();
		}

		#region 初始化列

		private void Init()
		{
			CreateColumn();

			if (this.checkBoxes)
			{
				this.StateImageList = this.stateImageList;
				CreateStateImages();
			}

			this.MouseClick += new MouseEventHandler(Tree_MouseClick);
		}

		private void CreateColumn()
		{
			TreeListColumn treeListColumn = new TreeListColumn();
			treeListColumn.OptionsColumn.ReadOnly = true;
			treeListColumn.Caption = "";
			treeListColumn.Name = "colName";
			treeListColumn.Visible = true;
			treeListColumn.VisibleIndex = 0;
			this.Columns.AddRange(new DevExpress.XtraTreeList.Columns.TreeListColumn[] { treeListColumn });
		}

		#endregion

		#region Tree_MouseClick

		private void Tree_MouseClick(object sender, MouseEventArgs e)
		{
			if (e.Button != MouseButtons.Left) //必须左键点击才有效
			{
				return;
			}

			//点击点必须是CheckBoxes框才有效
			TreeListHitInfo hInfo = this.CalcHitInfo(new Point(e.X, e.Y));
			if (hInfo.HitInfoType == HitInfoType.StateImage)
			{
				TreeListNode node = this.FocusedNode;
				switch (node.StateImageIndex)
				{
					case 0:
						node.StateImageIndex = (int)NodeState.Checked;
						break;

					case 1:
						node.StateImageIndex = (int)NodeState.Unchecked;
						break;

					case 2:
						node.StateImageIndex = (int)NodeState.Checked;
						break;
				}

				UpdateNodeState(node); //联动更新与当前节点相关的所有节点的状态
				RaiseNodeStateChanged(node);
			}
		}

		#endregion

		public virtual void BuildTree()
		{
			this.ClearNodes();
		}

		#region FillTree

		protected virtual void FillTree(IList list)
		{
			//Debug.Print("---------------");
			//Debug.Print("FillTree：" + DateTime.Now.ToLongTimeString());

			FillTree(this.Nodes[0], list);

			//Debug.Print("End FillTree：" + DateTime.Now.ToLongTimeString());

			//Debug.Print("");
			//Debug.Print("UpdateTreeState：" + DateTime.Now.ToLongTimeString());

			UpdateTreeState();

			//Debug.Print("End UpdateTreeState：" + DateTime.Now.ToLongTimeString());
		}

		private void FillTree(TreeListNode node, IList list)
		{
			object checkedValue = NodeExists(node, list);
			this.SetNode(node, checkedValue != null, checkedValue);

			foreach (TreeListNode child in node.Nodes)
			{
				FillTree(child, list);
			}
		}

		private object NodeExists(TreeListNode node, IList list)
		{
			if (list == null)
			{
				return null;
			}

			foreach (object item in list)
			{
				TagValue tagValue = node.Tag as TagValue;
				if (tagValue == null || tagValue.OriginValue == null)
				{
					return false;
				}

				if (TagValueCompare(tagValue.OriginValue, item))
				{
					//如果已经找到了对应的节点，那么不需要再匹配其它节点了
					//list.Remove(item);
					return item;
				}
			}
			return null;
		}

		//比较树节点的tag的值与itemValue的值是否相等
		//这个方法的返回值，决定了FillTree时，节点的状态
		protected virtual bool TagValueCompare(object tagValue, object itemValue)
		{
			return false;
		}

		#endregion

		#region AddNode、SetNode

		public TreeListNode AddNode(string text, TreeListNode parentNode, object tag)
		{
			TreeListNode node = this.AppendNode(new object[] { text }, parentNode, tag);
			node.StateImageIndex = (int)NodeState.Unchecked;
			node.Tag = new TagValue(tag);
			return node;
		}

		public void SetNode(TreeListNode node, bool checkedState)
		{
			node.StateImageIndex = checkedState ? (int)NodeState.Checked : (int)NodeState.Unchecked;
		}

		public void SetNode(TreeListNode node, bool checkedState, object checkedValue)
		{
			TagValue tagValue = node.Tag as TagValue;
			if (tagValue != null)
			{
				tagValue.CheckedValue = checkedValue;
			}
			SetNode(node, checkedState);
		}

		#endregion

		#region 外部属性、外部事件

		private bool checkBoxes = true;

		[DefaultValue(true)]
		public bool CheckBoxes
		{
			get
			{
				return checkBoxes;
			}
			set
			{
				checkBoxes = value;
			}
		}

		//节点状态改变事件
		public delegate void NodeStateChangedHandle(object sender, TreeListNode node, NodeState state);
		public event NodeStateChangedHandle NodeStateChanged;

		private void RaiseNodeStateChanged(TreeListNode node)
		{
			if (NodeStateChanged != null)
			{
				NodeState state = (NodeState)node.StateImageIndex;
				NodeStateChanged(this, node, state);
			}
		}

		#endregion

		#region CreateStateImages

		private ImageList stateImageList = new ImageList();

		private void CreateStateImages()
		{
			if (VisualStyleRenderer.IsSupported) //如果定操作系统使用视觉样式绘制控件
			{
				stateImageList.Images.Add(DrawVisualStyleBitmap(VisualStyleElement.Button.CheckBox.UncheckedNormal, stateImageList.ImageSize));
				stateImageList.Images.Add(DrawVisualStyleBitmap(VisualStyleElement.Button.CheckBox.CheckedNormal, stateImageList.ImageSize));
				stateImageList.Images.Add(DrawVisualStyleBitmap(VisualStyleElement.Button.CheckBox.MixedNormal, stateImageList.ImageSize));
			}
			else
			{
				stateImageList.Images.Add(DrawNormalBitmap(ButtonState.Normal, stateImageList.ImageSize));
				stateImageList.Images.Add(DrawNormalBitmap(ButtonState.Checked, stateImageList.ImageSize));
				stateImageList.Images.Add(DrawNormalBitmap(ButtonState.Pushed, stateImageList.ImageSize));
			}
		}

		private Bitmap DrawNormalBitmap(ButtonState buttonState, Size size)
		{
			Bitmap bitmap = new Bitmap(size.Width, size.Height);
			Graphics graphics = Graphics.FromImage(bitmap);
			Rectangle smallIconSize = new Rectangle(0, 0, size.Width, size.Height);

			ControlPaint.DrawMixedCheckBox(graphics, smallIconSize, buttonState);

			return bitmap;
		}

		private Bitmap DrawVisualStyleBitmap(VisualStyleElement checkBox, Size size)
		{
			Bitmap bitmap = new Bitmap(size.Width, size.Height);
			Graphics graphics = Graphics.FromImage(bitmap);
			Rectangle smallIconSize = new Rectangle(0, 0, size.Width, size.Height);

			VisualStyleRenderer vsr = new VisualStyleRenderer(VisualStyleElement.Button.CheckBox.UncheckedNormal);
			vsr.SetParameters(checkBox);
			vsr.DrawBackground(graphics, smallIconSize);

			return bitmap;
		}

		#endregion

		#region 联动更新与指定节点相关的所有节点的状态

		//更新节点选择的状态，同时更新与此节点相关的父节点和子节点的状态
		private void UpdateNodeState(TreeListNode node)
		{
			this.BeginUpdate();
			try
			{
				UpdateChildNodeState(node);
				UpdateParentNodeState(node);
			}
			finally
			{
				this.EndUpdate();
			}
		}

		//当前节点的状态与子节点的状态是同步的
		private void UpdateChildNodeState(TreeListNode node)
		{
			foreach (TreeListNode child in node.Nodes)
			{
				child.StateImageIndex = node.StateImageIndex;

				UpdateChildNodeState(child);
			}
		}

		//父节点的状态是由所有子节点的状态决定的
		//本函数判定原理：判断当前节点的所有同级节点（即父节点的所有子节点），是否与当前节点的状态一致，
		//如果全部一致，那么父节点即与当前节点状态一致，否则即为混合状态。
		private void UpdateParentNodeState(TreeListNode node)
		{
			if (node.ParentNode != null)
			{
				//默认为有子节点的状态不相同
				bool mixed = false;
				//逐一判断父节点的所有子节点
				foreach (TreeListNode child in node.ParentNode.Nodes)
				{
					//如果子节点的状态与当前子节点不一致，即为混合状态
					if (child.StateImageIndex != node.StateImageIndex)
					{
						mixed = true; //只要有一个状态不一致，即为混合状态
						break;
					}
				}
				//如果所有子节点的状态全部一致，那么父节点即为当前子节点的状态；否则为混合状态
				if (mixed)
				{
					node.ParentNode.StateImageIndex = (int)NodeState.Mixed;
				}
				else
				{
					node.ParentNode.StateImageIndex = node.StateImageIndex;
				}

				UpdateParentNodeState(node.ParentNode);
			}
		}

		#endregion

		#region 以非联动的方式更新树的所有节点的状态

		public void UpdateTreeState()
		{
			UpdateTreeState(this.Nodes[0]);
		}

		private void UpdateTreeState(TreeListNode node)
		{
			if (node.Nodes.Count == 0) //如果当前节点是最后一个节点，
			{
				UpdateParentNodeState(node); //那么联动更新它的父节点
			}

			foreach (TreeListNode child in node.Nodes)
			{
				UpdateTreeState(child);
			}
		}

		#endregion

		#region 获取用户选中的节点

		//获取选中的节点
		public List<TreeListNode> GetCheckedNodeList(bool allowTagNull)
		{
			List<TreeListNode> list = new List<TreeListNode>();
			GetCheckedNode(allowTagNull, this.Nodes[0], list);
			return list;
		}

		private void GetCheckedNode(bool allowTagNull, TreeListNode node, List<TreeListNode> list)
		{
			if (node.StateImageIndex == (int)NodeState.Checked)
			{
				if (node.Tag == null && allowTagNull)
				{
					list.Add(node);
				}
			}

			foreach (TreeListNode child in node.Nodes)
			{
				GetCheckedNode(allowTagNull, child, list);
			}
		}

		//获取选中的节点的Tag数据列表
		public List<T> GetCheckedDataList<T>()
		{
			List<T> list = new List<T>();
			GetCheckedData(this.Nodes[0], list);
			return list;
		}

		private void GetCheckedData<T>(TreeListNode node, List<T> list)
		{
			if (node.StateImageIndex == (int)NodeState.Checked)
			{
				TagValue tagValue = node.Tag as TagValue;
				if (tagValue != null)
				{
					//优先取CheckedValue
					if (tagValue.CheckedValue != null && tagValue.CheckedValue is T)
					{
						list.Add((T)tagValue.CheckedValue);
					}
					else if (tagValue.OriginValue != null && tagValue.OriginValue is T)
					{
						list.Add((T)tagValue.OriginValue);
					}
				}
			}

			foreach (TreeListNode child in node.Nodes)
			{
				GetCheckedData(child, list);
			}
		}

		#endregion
	}
}
