// --------------------------------------------------------------------------------------------- #region // Copyright (c) 2004-2005, SIL International. All Rights Reserved. // <copyright from='2004' to='2005' company='SIL International'> // Copyright (c) 2004-2005, SIL International. All Rights Reserved. // // Distributable under the terms of either the Common Public License or the // GNU Lesser General Public License, as specified in the LICENSING.txt file. // </copyright> #endregion // // File: TriStateTreeView.cs // Responsibility: Eberhard Beilharz/Tim Steenwyk // http://www.codeproject.com/Articles/6549/Tri-State-TreeView-Control // <remarks> // </remarks> // --------------------------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using Skybound.VisualStyles; using System.Runtime.InteropServices; using System.Collections; namespace TriStateTreeView { public partial class TriStateTreeView : TreeView { /// <summary> /// 塗聚文 20121122 修改 /// 捷為工作室 /// ///TreeView The check state /// </summary> /// <remarks>The states corresponds to image index</remarks> public enum CheckState { /// <summary>greyed out</summary> GreyChecked = 0, /// <summary>Unchecked</summary> Unchecked = 1, /// <summary>Checked</summary> Checked = 2, } #region Redefined Win-API structs and methods /// <summary></summary> [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TV_HITTESTINFO { /// <summary>Client coordinates of the point to test.</summary> public Point pt; /// <summary>Variable that receives information about the results of a hit test.</summary> public TVHit flags; /// <summary>Handle to the item that occupies the point.</summary> public IntPtr hItem; } /// <summary>Hit tests for tree view</summary> [Flags] public enum TVHit { /// <summary>In the client area, but below the last item.</summary> NoWhere = 0x0001, /// <summary>On the bitmap associated with an item.</summary> OnItemIcon = 0x0002, /// <summary>On the label (string) associated with an item.</summary> OnItemLabel = 0x0004, /// <summary>In the indentation associated with an item.</summary> OnItemIndent = 0x0008, /// <summary>On the button associated with an item.</summary> OnItemButton = 0x0010, /// <summary>In the area to the right of an item. </summary> OnItemRight = 0x0020, /// <summary>On the state icon for a tree-view item that is in a user-defined state.</summary> OnItemStateIcon = 0x0040, /// <summary>On the bitmap or label associated with an item. </summary> OnItem = (OnItemIcon | OnItemLabel | OnItemStateIcon), /// <summary>Above the client area. </summary> Above = 0x0100, /// <summary>Below the client area.</summary> Below = 0x0200, /// <summary>To the right of the client area.</summary> ToRight = 0x0400, /// <summary>To the left of the client area.</summary> ToLeft = 0x0800 } /// <summary></summary> public enum TreeViewMessages { /// <summary></summary> TV_FIRST = 0x1100, // TreeView messages /// <summary></summary> TVM_HITTEST = (TV_FIRST + 17), } /// <summary></summary> [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, ref TV_HITTESTINFO lParam); #endregion /// <summary> /// /// </summary> public TriStateTreeView() { InitializeComponent(); if (ThemeInformation.VisualStylesEnabled) { Bitmap bmp = new Bitmap(m_TriStateImages.ImageSize.Width, m_TriStateImages.ImageSize.Height); Rectangle rc = new Rectangle(0, 0, bmp.Width, bmp.Height); Graphics graphics = Graphics.FromImage(bmp); ThemePaint.Draw(graphics, this, ThemeClasses.Button, ThemeParts.ButtonCheckBox, ThemeStates.CheckBoxCheckedDisabled, rc, rc); m_TriStateImages.Images[0] = bmp; ThemePaint.Draw(graphics, this, ThemeClasses.Button, ThemeParts.ButtonCheckBox, ThemeStates.CheckBoxUncheckedNormal, rc, rc); m_TriStateImages.Images[1] = bmp; ThemePaint.Draw(graphics, this, ThemeClasses.Button, ThemeParts.ButtonCheckBox, ThemeStates.CheckBoxCheckedNormal, rc, rc); m_TriStateImages.Images[2] = bmp; } ImageList = m_TriStateImages; ImageIndex = (int)CheckState.Unchecked; SelectedImageIndex = (int)CheckState.Unchecked; } #region Hide no longer appropriate properties from Designer /// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// ------------------------------------------------------------------------------------ [Browsable(false)] public new bool CheckBoxes { get { return base.CheckBoxes; } set { base.CheckBoxes = value; } } /// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// ------------------------------------------------------------------------------------ [Browsable(false)] public new int ImageIndex { get { return base.ImageIndex; } set { base.ImageIndex = value; } } /// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// ------------------------------------------------------------------------------------ [Browsable(false)] public new ImageList ImageList { get { return base.ImageList; } set { base.ImageList = value; } } /// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// ------------------------------------------------------------------------------------ [Browsable(false)] public new int SelectedImageIndex { get { return base.SelectedImageIndex; } set { base.SelectedImageIndex = value; } } #endregion #region Overrides /// ------------------------------------------------------------------------------------ /// <summary> /// Called when the user clicks on an item /// </summary> /// <param name="e"></param> /// ------------------------------------------------------------------------------------ protected override void OnClick(EventArgs e) { base.OnClick(e); TV_HITTESTINFO hitTestInfo = new TV_HITTESTINFO(); hitTestInfo.pt = PointToClient(Control.MousePosition); SendMessage(Handle, TreeViewMessages.TVM_HITTEST, 0, ref hitTestInfo); if ((hitTestInfo.flags & TVHit.OnItemIcon) == TVHit.OnItemIcon) { TreeNode node = GetNodeAt(hitTestInfo.pt); if (node != null) ChangeNodeState(node); } } /// ------------------------------------------------------------------------------------ /// <summary> /// Toggle item if user presses space bar /// </summary> /// <param name="e"></param> /// ------------------------------------------------------------------------------------ protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.KeyCode == Keys.Space) ChangeNodeState(SelectedNode); } #endregion #region Private methods /// ------------------------------------------------------------------------------------ /// <summary> /// Checks or unchecks all children /// </summary> /// <param name="node"></param> /// <param name="state"></param> /// ------------------------------------------------------------------------------------ private void CheckNode(TreeNode node, CheckState state) { InternalSetChecked(node, state); foreach (TreeNode child in node.Nodes) CheckNode(child, state); } /// ------------------------------------------------------------------------------------ /// <summary> /// Called after a node changed its state. Has to go through all direct children and /// set state based on children's state. /// </summary> /// <param name="node">Parent node</param> /// ------------------------------------------------------------------------------------ private void ChangeParent(TreeNode node) { if (node == null) return; CheckState state = GetChecked(node.FirstNode); foreach (TreeNode child in node.Nodes) state &= GetChecked(child); if (InternalSetChecked(node, state)) ChangeParent(node.Parent); } /// ------------------------------------------------------------------------------------ /// <summary> /// Handles changing the state of a node /// </summary> /// <param name="node"></param> /// ------------------------------------------------------------------------------------ protected void ChangeNodeState(TreeNode node) { BeginUpdate(); CheckState newState; if (node.ImageIndex == (int)CheckState.Unchecked || node.ImageIndex < 0) newState = CheckState.Checked; else newState = CheckState.Unchecked; CheckNode(node, newState); ChangeParent(node.Parent); EndUpdate(); } /// ------------------------------------------------------------------------------------ /// <summary> /// Sets the checked state of a node, but doesn't deal with children or parents /// </summary> /// <param name="node">Node</param> /// <param name="state">The new checked state</param> /// <returns><c>true</c> if checked state was set to the requested state, otherwise /// <c>false</c>.</returns> /// ------------------------------------------------------------------------------------ private bool InternalSetChecked(TreeNode node, CheckState state) { TreeViewCancelEventArgs args = new TreeViewCancelEventArgs(node, false, TreeViewAction.Unknown); OnBeforeCheck(args); if (args.Cancel) return false; node.ImageIndex = (int)state; node.SelectedImageIndex = (int)state; OnAfterCheck(new TreeViewEventArgs(node, TreeViewAction.Unknown)); return true; } /// ------------------------------------------------------------------------------------ /// <summary> /// Build a list of all of the tag data for checked items in the tree. /// </summary> /// <param name="node"></param> /// <param name="list"></param> /// ------------------------------------------------------------------------------------ private void BuildTagDataList(TreeNode node, ArrayList list) { if (GetChecked(node) == CheckState.Checked && node.Tag != null) list.Add(node.Tag); foreach (TreeNode child in node.Nodes) BuildTagDataList(child, list); } /// <summary> /// 選擇的節點的集合 /// </summary> /// <param name="node"></param> /// <param name="selectedNodes"></param> private void getBuildTagDataList(TreeNodeCollection nodes, List<TreeNode> selectedNodes) { foreach (TreeNode node in nodes) { if (GetChecked(node) == CheckState.Checked && node.Tag != null) { selectedNodes.Add(node); } getBuildTagDataList(node.Nodes, selectedNodes); } } /// <summary> /// 選擇的節點的集合 /// </summary> /// <param name="nodes"></param> /// <param name="selectedNodes"></param> public void GetSelectedTreeNode(TreeNodeCollection nodes, List<TreeNode> selectedNodes) { foreach (TreeNode node in nodes) { if (GetChecked(node) == CheckState.Checked && node.Tag != null)//node.Checked { selectedNodes.Add(node); } GetSelectedTreeNode(node.Nodes, selectedNodes); } } /// ------------------------------------------------------------------------------------ /// <summary> /// Look through the tree nodes to find the node that has given tag data and check it. /// </summary> /// <param name="node"></param> /// <param name="tag"></param> /// <param name="state"></param> /// ------------------------------------------------------------------------------------ private void FindAndCheckNode(TreeNode node, object tag, CheckState state) { if (node.Tag != null && node.Tag.Equals(tag)) { SetChecked(node, state); return; } foreach (TreeNode child in node.Nodes) FindAndCheckNode(child, tag, state); } #endregion #region Public methods /// ------------------------------------------------------------------------------------ /// <summary> /// Gets the checked state of a node /// </summary> /// <param name="node">Node</param> /// <returns>The checked state</returns> /// ------------------------------------------------------------------------------------ public CheckState GetChecked(TreeNode node) { if (node.ImageIndex < 0) return CheckState.Unchecked; else return (CheckState)node.ImageIndex; } /// ------------------------------------------------------------------------------------ /// <summary> /// Sets the checked state of a node /// </summary> /// <param name="node">Node</param> /// <param name="state">The new checked state</param> /// ------------------------------------------------------------------------------------ public void SetChecked(TreeNode node, CheckState state) { if (!InternalSetChecked(node, state)) return; CheckNode(node, state); ChangeParent(node.Parent); } /// ------------------------------------------------------------------------------------ /// <summary> /// Find a node in the tree that matches the given tag data and set its checked state /// </summary> /// <param name="tag"></param> /// <param name="state"></param> /// ------------------------------------------------------------------------------------ public void CheckNodeByTag(object tag, CheckState state) { if (tag == null) return; foreach (TreeNode node in Nodes) FindAndCheckNode(node, tag, state); } /// ------------------------------------------------------------------------------------ /// <summary> /// Return a list of the tag data for all of the checked items in the tree /// </summary> /// <returns></returns> /// ------------------------------------------------------------------------------------ public ArrayList GetCheckedTagData() { ArrayList list = new ArrayList(); foreach (TreeNode node in Nodes) BuildTagDataList(node, list); return list; } /// <summary> /// /// </summary> /// <param name="nodes"></param> /// <returns></returns> public List<TreeNode> GetSelectedNodes(TreeNodeCollection nodes) { List<TreeNode> selectedNodes = new List<TreeNode>(); getBuildTagDataList(nodes, selectedNodes); return selectedNodes; } #endregion } }