简单的三态树状视图






4.94/5 (26投票s)
提供另一种简单的方法来获得三态树视图,确保与所有 Windows UI / 风格兼容

引言
Microsoft 提供了一个 TreeView
控件,它无法显示树状态复选框。 有很多方法可以实现此功能,例如窗口过程钩取和其他操作。 本文将向您展示另一种更简单的方法,仅使用 .NET Framework 提供的技术。
此控件仅继承原始树视图控件,用 StateImageList
替换 CheckBoxes
属性的使用,并主要重写节点单击事件过程以处理设置三态。
为了符合所有 Windows UI 样式,状态图像列表将在创建控件时使用 .NET Framework 提供的 CheckBoxRenderer
类动态构建。
背景
可能需要一个树视图控件在父节点上显示,这些父节点的子节点并非都具有相同的选中状态。 此外,可能需要,在选中子/超节点时,在树中上下刷新选中状态。
使用诸如窗口过程钩取之类的技术并不适合所有人,因为它并不总是容易调试。 此外,使用静态图像将避免在不同的 Windows UI 上显示正确的复选框。
使用控件 / 代码
将 TriStateCheckBoxTreeView.cs 添加到您的项目中,然后只需将控件拖放到表单上即可。 您将在属性网格中找到与 .NET Framework 提供的普通树视图控件的区别。 现在有一个属性可以直接在 CheckBoxes
属性下方启用三态使用。

使用树节点的 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 日:更新演示项目