use*_*312 12 .net c# treeview .net-2.0 winforms
以下代码旨在根据需要以递归方式检查或取消检查父节点或子节点.
例如,在此位置,如果我们取消选中其中任何一个节点,则必须取消选中A,G,L和T节点.
以下代码的问题是,每当我双击任何节点时,算法都无法实现其目的.
树搜索算法从这里开始:
// stack is used to traverse the tree iteratively.
Stack<TreeNode> stack = new Stack<TreeNode>();
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
TreeNode selectedNode = e.Node;
bool checkedStatus = e.Node.Checked;
// suppress repeated even firing
treeView1.AfterCheck -= treeView1_AfterCheck;
// traverse children
stack.Push(selectedNode);
while(stack.Count > 0)
{
TreeNode node = stack.Pop();
node.Checked = checkedStatus;
System.Console.Write(node.Text + ", ");
if (node.Nodes.Count > 0)
{
ICollection tnc = node.Nodes;
foreach (TreeNode n in tnc)
{
stack.Push(n);
}
}
}
//traverse parent
while(selectedNode.Parent!=null)
{
TreeNode node = selectedNode.Parent;
node.Checked = checkedStatus;
selectedNode = selectedNode.Parent;
}
// "suppress repeated even firing" ends here
treeView1.AfterCheck += treeView1_AfterCheck;
string str = string.Empty;
}
Run Code Online (Sandbox Code Playgroud)
司机计划
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
#region MyRegion
private void button1_Click(object sender, EventArgs e)
{
TreeNode a = new TreeNode("A");
TreeNode b = new TreeNode("B");
TreeNode c = new TreeNode("C");
TreeNode d = new TreeNode("D");
TreeNode g = new TreeNode("G");
TreeNode h = new TreeNode("H");
TreeNode i = new TreeNode("I");
TreeNode j = new TreeNode("J");
TreeNode k = new TreeNode("K");
TreeNode l = new TreeNode("L");
TreeNode m = new TreeNode("M");
TreeNode n = new TreeNode("N");
TreeNode o = new TreeNode("O");
TreeNode p = new TreeNode("P");
TreeNode q = new TreeNode("Q");
TreeNode r = new TreeNode("R");
TreeNode s = new TreeNode("S");
TreeNode t = new TreeNode("T");
TreeNode u = new TreeNode("U");
TreeNode v = new TreeNode("V");
TreeNode w = new TreeNode("W");
TreeNode x = new TreeNode("X");
TreeNode y = new TreeNode("Y");
TreeNode z = new TreeNode("Z");
k.Nodes.Add(x);
k.Nodes.Add(y);
l.Nodes.Add(s);
l.Nodes.Add(t);
l.Nodes.Add(u);
n.Nodes.Add(o);
n.Nodes.Add(p);
n.Nodes.Add(q);
n.Nodes.Add(r);
g.Nodes.Add(k);
g.Nodes.Add(l);
i.Nodes.Add(m);
i.Nodes.Add(n);
j.Nodes.Add(b);
j.Nodes.Add(c);
j.Nodes.Add(d);
a.Nodes.Add(g);
a.Nodes.Add(h);
a.Nodes.Add(i);
a.Nodes.Add(j);
treeView1.Nodes.Add(a);
treeView1.ExpandAll();
button1.Enabled = false;
}
#endregion
Run Code Online (Sandbox Code Playgroud)
预计会发生:
看一下应用程序的屏幕截图.甲,G ^,大号和Ť被检查.如果我取消选中,比如说L,
- T应该是未选中的,因为T是L的孩子.
- 应该取消选中G和A,因为他们没有孩子.
怎么了:
如果我单击任何节点,此应用程序代码工作正常.如果我双击某个节点,该节点将变为选中/取消选中,但相同的更改不会反映在父节点和子节点上.
双击也会冻结应用程序一段时间.
如何解决此问题并获得预期的行为?
Rez*_*aei 12
这些是这里要解决的主要问题:
防止AfterCkeck
事件处理程序递归地重复逻辑.
当您更改Checked
节点的属性时AfterCheck
,它会导致另一个AfterCheck
事件,这可能导致我们的堆栈溢出或至少在检查事件后不必要或我们的算法中的不可预测的结果.
修复DoubleClick
了复选框错误TreeView
.
当您双击CheckBox
in时TreeView
,该Checked
值的值Node
将更改两次,并且在双击之前将设置为原始状态,但该AfterCheck
事件将引发一次.
获取节点的后代和祖先的扩展方法
我们需要创建方法来获取节点的后代和祖先.为此,我们将为TreeNode
类创建扩展方法.
实现算法
解决上述问题后,正确的算法将导致我们期望的点击.这是期望:
检查/取消选中节点时:
在我们解决了上述问题并创建Descendants
和Ancestors
遍历树之后,我们就足以处理AfterCheck
事件并具有以下逻辑:
e.Node.Descendants().ToList().ForEach(x =>
{
x.Checked = e.Node.Checked;
});
e.Node.Ancestors().ToList().ForEach(x =>
{
x.Checked = x.Descendants().ToList().Any(y => y.Checked);
});
Run Code Online (Sandbox Code Playgroud)
下载
您可以从以下存储库下载工作示例:
防止AfterCkeck
事件处理程序递归地重复逻辑
事实上,我们不会阻止AfterCheck
事件处理程序提升AfterCheck
.相反,我们检测AfterCheck
是由用户还是由处理程序内的代码引发的.为此,我们可以检查Action
事件arg的属性:
要防止多次引发事件,请向事件处理程序添加逻辑,该事件处理程序仅
Action
在TreeViewEventArgs
未设置为 的属性时才执行递归代码TreeViewAction.Unknown
.
private void exTreeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action != TreeViewAction.Unknown)
{
// Changing Checked
}
}
Run Code Online (Sandbox Code Playgroud)
修复DoubleClick
了复选框错误TreeView
正如也提到这个帖子,有一个错误TreeView
,当你双击CheckBox
的TreeView
,该Checked
值Node
将两次改变之前双击将被设置为原来的状态,但AfterCheck
事件会引发一次.
要解决此问题,您可以查看WM_LBUTTONDBLCLK
消息并检查是否双击复选框,忽略它:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public class ExTreeView : TreeView
{
private const int WM_LBUTTONDBLCLK = 0x0203;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_LBUTTONDBLCLK)
{
var info = this.HitTest(PointToClient(Cursor.Position));
if (info.Location == TreeViewHitTestLocations.StateImage)
{
m.Result = IntPtr.Zero;
return;
}
}
base.WndProc(ref m);
}
}
Run Code Online (Sandbox Code Playgroud)
获取节点的后代和祖先的扩展方法
要获取节点的后代和祖先,我们需要创建一些扩展方法AfterCheck
来实现该算法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public static class Extensions
{
public static List<TreeNode> Descendants(this TreeView tree)
{
var nodes = tree.Nodes.Cast<TreeNode>();
return nodes.SelectMany(x => x.Descendants()).Concat(nodes).ToList();
}
public static List<TreeNode> Descendants(this TreeNode node)
{
var nodes = node.Nodes.Cast<TreeNode>().ToList();
return nodes.SelectMany(x => Descendants(x)).Concat(nodes).ToList();
}
public static List<TreeNode> Ancestors(this TreeNode node)
{
return AncestorsInternal(node).ToList();
}
private static IEnumerable<TreeNode> AncestorsInternal(TreeNode node)
{
while (node.Parent != null)
{
node = node.Parent;
yield return node;
}
}
}
Run Code Online (Sandbox Code Playgroud)
实现算法
使用上面的扩展方法,我将处理AfterCheck
事件,因此当您选中/取消选中一个节点时:
这是实施:
private void exTreeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action != TreeViewAction.Unknown)
{
e.Node.Descendants().ToList().ForEach(x =>
{
x.Checked = e.Node.Checked;
});
e.Node.Ancestors().ToList().ForEach(x =>
{
x.Checked = x.Descendants().ToList().Any(y => y.Checked);
});
}
}
Run Code Online (Sandbox Code Playgroud)
例
要测试解决方案,您可以填写TreeView
以下数据:
private void Form1_Load(object sender, EventArgs e)
{
exTreeView1.Nodes.Clear();
exTreeView1.Nodes.AddRange(new TreeNode[] {
new TreeNode("1", new TreeNode[] {
new TreeNode("11", new TreeNode[]{
new TreeNode("111"),
new TreeNode("112"),
}),
new TreeNode("12", new TreeNode[]{
new TreeNode("121"),
new TreeNode("122"),
new TreeNode("123"),
}),
}),
new TreeNode("2", new TreeNode[] {
new TreeNode("21", new TreeNode[]{
new TreeNode("211"),
new TreeNode("212"),
}),
new TreeNode("22", new TreeNode[]{
new TreeNode("221"),
new TreeNode("222"),
new TreeNode("223"),
}),
})
});
exTreeView1.ExpandAll();
}
Run Code Online (Sandbox Code Playgroud)
由于.NET 2没有linq扩展方法,对于那些有兴趣在.NET 2中使用该功能的人(包括原始海报),这里是.NET 2.0中的代码:
ExTreeView
using System;
using System.Collections.Generic;
using System.Windows.Forms;
public class ExTreeView : TreeView
{
private const int WM_LBUTTONDBLCLK = 0x0203;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_LBUTTONDBLCLK) {
var info = this.HitTest(PointToClient(Cursor.Position));
if (info.Location == TreeViewHitTestLocations.StateImage) {
m.Result = IntPtr.Zero;
return;
}
}
base.WndProc(ref m);
}
public IEnumerable<TreeNode> Ancestors(TreeNode node)
{
while (node.Parent != null) {
node = node.Parent;
yield return node;
}
}
public IEnumerable<TreeNode> Descendants(TreeNode node)
{
foreach (TreeNode c1 in node.Nodes) {
yield return c1;
foreach (TreeNode c2 in Descendants(c1)) {
yield return c2;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
AfterSelect
private void exTreeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action != TreeViewAction.Unknown) {
foreach (TreeNode x in exTreeView1.Descendants(e.Node)) {
x.Checked = e.Node.Checked;
}
foreach (TreeNode x in exTreeView1.Ancestors(e.Node)) {
bool any = false;
foreach (TreeNode y in exTreeView1.Descendants(x))
any = any || y.Checked;
x.Checked = any;
};
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
463 次 |
最近记录: |