You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/Module/GeoSigmaViewer/TriStateTreeView.cs

335 lines
11 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace GeoSigmaViewer
{
public static class ClassExtensions
{
/// <summary>
/// 选中状态0-未选中, 1-选中2-部分选中。
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public static int GetCheckStatus(this TreeNode node)
{
return node.StateImageIndex;
}
public static void SetCheckStatus(this TreeNode node, int status)
{
node.StateImageIndex=status;
}
}
public partial class TriStateTreeView : TreeView
{
private const int STATE_UNCHECKED = 0; //unchecked state
private const int STATE_CHECKED = 1; //checked state
private const int STATE_MIXED = 2; //mixed state (indeterminate)
private bool isMouseOnStatus = false;
//create a new ThreeStateTreeView
public int ID { get; set; }
public TriStateTreeView() : base()
{
System.Security.Cryptography.RNGCryptoServiceProvider csp = new System.Security.Cryptography.RNGCryptoServiceProvider();
byte[] byteCsp = new byte[10];
csp.GetBytes(byteCsp);
ID=BitConverter.ToInt32(byteCsp, 0);
}
protected override void OnBeforeCollapse(TreeViewCancelEventArgs e)
{
if (isMouseOnStatus)
e.Cancel=true;
}
protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
if (isMouseOnStatus)
e.Cancel=true;
}
/// <summary>
/// 重新设置所有节点的选中状态
/// </summary>
public void UpdateView()
{
this.SetNodesState(this.Nodes);
}
public void SelectAll()
{
this.SetNodesState(this.Nodes, STATE_CHECKED);
}
public void UnselectAll()
{
this.SetNodesState(this.Nodes, STATE_UNCHECKED);
}
public void AntiSelect()
{
this.SetNodesToAntiState(this.Nodes);
}
//returns a value indicating if all children are checked
public static bool IsAllChildrenChecked(TreeNode parent)
{
return IsAllChildrenSame(parent, STATE_CHECKED);
}
//returns a value indicating if all children are unchecked
public static bool IsAllChildrenUnchecked(TreeNode parent)
{
return IsAllChildrenSame(parent, STATE_UNCHECKED);
}
//initialize all nodes state image
private void SetNodesState(TreeNodeCollection nodes, int state)
{
foreach (TreeNode node in nodes)
{
node.StateImageIndex=state;
if (state==STATE_CHECKED)
{
node.Checked=true;
}
else
{
node.Checked=false;
}
if (node.Nodes.Count!=0)
{
SetNodesState(node.Nodes, state);
}
}
}
private void UpdateStatus(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
UpdateParent(node);
if (node.Nodes.Count!=0)
{
UpdateStatus(node.Nodes);
}
}
}
private void SetNodesState(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
//if (node.Checked)
//{
// node.StateImageIndex=STATE_CHECKED;
//}
//else
//{
// node.StateImageIndex=STATE_UNCHECKED;
//}
UpdateParent(node);
if (node.Nodes.Count!=0)
{
SetNodesState(node.Nodes);
}
}
}
private void SetNodesToAntiState(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
if (node.GetCheckStatus()==STATE_CHECKED)
{
node.SetCheckStatus(STATE_UNCHECKED);
//node.Checked=true;
}
else if (node.StateImageIndex==STATE_UNCHECKED)
{
node.StateImageIndex=STATE_CHECKED;
//node.Checked=false;
}
if (node.Nodes.Count!=0)
{
SetNodesToAntiState(node.Nodes);
}
}
}
//update children state image with the parent value
private void UpdateChildren(TreeNode parent)
{
int state = parent.StateImageIndex;
foreach (TreeNode node in parent.Nodes)
{
node.StateImageIndex=state;
if (node.Nodes.Count!=0)
{
UpdateChildren(node);
}
}
}
//update parent state image base on the children state
private void UpdateParent(TreeNode child)
{
TreeNode parent = child.Parent;
if (parent==null)
{
return;
}
if (child.StateImageIndex==STATE_MIXED)
{
parent.StateImageIndex=STATE_MIXED;
}
else if (IsAllChildrenChecked(parent))
{
parent.StateImageIndex=STATE_CHECKED;
}
else if (IsAllChildrenUnchecked(parent))
{
parent.StateImageIndex=STATE_UNCHECKED;
}
else
{
parent.StateImageIndex=STATE_MIXED;
}
UpdateParent(parent);
}
//returns a value indicating if all children are in the same state
private static bool IsAllChildrenSame(TreeNode parent, int state)
{
foreach (TreeNode node in parent.Nodes)
{
if (node.StateImageIndex!=state)
{
return false;
}
if (node.Nodes.Count!=0&&!IsAllChildrenSame(node, state))
{
return false;
}
}
return true;
}
//build the checked, unchecked and indeterminate images
private static Image GetStateImage(CheckBoxState state, Size imageSize)
{
Bitmap bmp = new Bitmap(16, 16);
using (Graphics g = Graphics.FromImage(bmp))
{
Point pt = new Point((16-imageSize.Width)/2, (16-imageSize.Height)/2);
CheckBoxRenderer.DrawCheckBox(g, pt, state);
}
return bmp;
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
this.StateImageList=new ImageList();
using (Graphics g = base.CreateGraphics())
{
Size glyphSize = CheckBoxRenderer.GetGlyphSize(g, CheckBoxState.UncheckedNormal);
this.StateImageList.Images.Add(GetStateImage(CheckBoxState.UncheckedNormal, glyphSize));
this.StateImageList.Images.Add(GetStateImage(CheckBoxState.CheckedNormal, glyphSize));
this.StateImageList.Images.Add(GetStateImage(CheckBoxState.MixedNormal, glyphSize));
this.StateImageList.Images.Add(GetStateImage(CheckBoxState.UncheckedDisabled, glyphSize));
this.StateImageList.Images.Add(GetStateImage(CheckBoxState.CheckedDisabled, glyphSize));
this.StateImageList.Images.Add(GetStateImage(CheckBoxState.MixedDisabled, glyphSize));
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
MouseEventArgs ee = e as MouseEventArgs;
if (ee.Button==MouseButtons.Left)
{
TreeViewHitTestInfo info = base.HitTest(ee.Location);
if (info.Node!=null&&info.Location==TreeViewHitTestLocations.StateImage)
{
isMouseOnStatus=true;
TreeNode node = info.Node;
switch (node.StateImageIndex)
{
case STATE_UNCHECKED:
case STATE_MIXED:
node.StateImageIndex=STATE_CHECKED;
break;
case STATE_CHECKED:
node.StateImageIndex=STATE_UNCHECKED;
break;
}
UpdateChildren(node);
UpdateParent(node);
return;
}
else
{
isMouseOnStatus=false;
}
}
base.OnMouseDown(e);
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
}
//check if user press the space key
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode==Keys.Space)
{
if (base.SelectedNode!=null)
{
TreeNode node = base.SelectedNode;
switch (node.StateImageIndex)
{
case STATE_UNCHECKED:
case STATE_MIXED:
node.StateImageIndex=STATE_CHECKED;
break;
case STATE_CHECKED:
node.StateImageIndex=STATE_UNCHECKED;
break;
}
UpdateChildren(node);
UpdateParent(node);
}
}
}
//swap between enabled and disabled images
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
for (int i = 0; i<3; i++)
{
Image img = this.StateImageList.Images[0];
this.StateImageList.Images.RemoveAt(0);
this.StateImageList.Images.Add(img);
}
}
}
}