65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的三态树状视图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (26投票s)

2010年2月18日

CPOL

2分钟阅读

viewsIcon

181231

downloadIcon

6837

提供另一种简单的方法来获得三态树视图,确保与所有 Windows UI / 风格兼容

TriStateTreeView

引言

Microsoft 提供了一个 TreeView 控件,它无法显示树状态复选框。 有很多方法可以实现此功能,例如窗口过程钩取和其他操作。 本文将向您展示另一种更简单的方法,仅使用 .NET Framework 提供的技术。

此控件仅继承原始树视图控件,用 StateImageList 替换 CheckBoxes 属性的使用,并主要重写节点单击事件过程以处理设置三态。
为了符合所有 Windows UI 样式,状态图像列表将在创建控件时使用 .NET Framework 提供的 CheckBoxRenderer 类动态构建。

背景

可能需要一个树视图控件在父节点上显示,这些父节点的子节点并非都具有相同的选中状态。 此外,可能需要,在选中子/超节点时,在树中上下刷新选中状态。

使用诸如窗口过程钩取之类的技术并不适合所有人,因为它并不总是容易调试。 此外,使用静态图像将避免在不同的 Windows UI 上显示正确的复选框。

使用控件 / 代码

TriStateCheckBoxTreeView.cs 添加到您的项目中,然后只需将控件拖放到表单上即可。 您将在属性网格中找到与 .NET Framework 提供的普通树视图控件的区别。 现在有一个属性可以直接在 CheckBoxes 属性下方启用三态使用。

PropertyGrid.png

使用树节点的 Checked 属性进行常规检查状态检查。 要确定所有三种可能的状态,您可以检查 StateImageIndex,它给出以下三个可能的索引

  • 0 - 未选中
  • 1 - 选中
  • 2 - 混合

使用 CheckBoxRenderer 渲染复选框

Bitmap GetCheckBoxBitmap(CheckBoxState myState)
{
    Bitmap bmpCheckBox = new Bitmap(16, 16);
    Graphics gfxCheckBox = Graphics.FromImage(bmpCheckBox);
    CheckBoxRenderer.DrawCheckBox(gfxCheckBox, new Point(2, 2), myState);
    gfxCheckBox.Save();
 
    return bmpCheckBox;
}

设置三态

protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
{
 Stack<TreeNode> stNodes;
 TreeNode tnBuffer;
 bool bMixedState;
 int iSpacing;
 int iIndex;
 
    base.OnNodeMouseClick(e);
    iSpacing = ImageList == null ? 0 : 18;			// if user clicked area
    if (e.X > e.Node.Bounds.Left - iSpacing ||		// *not* used by the state
        e.X < e.Node.Bounds.Left - (iSpacing + 16))		// image we can leave here.
    { return; }
 
    tnBuffer = e.Node;					// buffer clicked node and
    tnBuffer.Checked = !tnBuffer.Checked;			// flip its check state.
 
    stNodes = new Stack<TreeNode>(tnBuffer.Nodes.Count);	// create a new stack and
    stNodes.Push(tnBuffer);					// push buffered node first.
    do {							// let's pop node from stack,
        tnBuffer = stNodes.Pop();				// inherit buffered node's
	tnBuffer.Checked = e.Node.Checked;			// check state and push
        for (int i = 0; i < tnBuffer.Nodes.Count; i++)		// each child on the stack
            stNodes.Push(tnBuffer.Nodes[i]);			// until there is no node
    } while (stNodes.Count > 0);				// left.
			
    bMixedState = false;
    tnBuffer = e.Node;					// re-buffer clicked node.
    while (tnBuffer.Parent != null) {			// while we get a parent we
        foreach (TreeNode tnChild in tnBuffer.Parent.Nodes)	// determine mixed check states
            bMixedState |= (tnChild.Checked != tnBuffer.Checked);// and convert current check
        iIndex = (int)Convert.ToUInt32(tnBuffer.Checked);	// state to state image index.
        tnBuffer.Parent.Checked = bMixedState || (iIndex > 0);	// set parent's check state and
        if (bMixedState)					// state image in dependency
            tnBuffer.Parent.StateImageIndex = CheckBoxesTriState ? 2 : 1;
        else						// of mixed state.
            tnBuffer.Parent.StateImageIndex = iIndex;
        tnBuffer = tnBuffer.Parent;				// finally buffer parent and
    }							// loop here.
}

限制

此代码已尽可能简单地编写。 您应该牢记一个限制: 如果您从代码添加节点,则必须在添加完成后调用控件的 Refresh() 以确保所有节点都具有正确的状态。

历史

  • 2010 年 2 月 17 日:第一个版本
  • 2010 年 2 月 18 日:扩展“简介”、“背景”、“使用控件/代码”部分,修复演示项目
  • 2010 年 3 月 18 日:对注释进行小的修饰
  • 2011 年 3 月 30 日:更新演示项目
© . All rights reserved.