|
|
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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|