【发布时间】:2010-07-09 17:38:58
【问题描述】:
我正在编写一个简单的扩展方法来对控件及其所有子控件执行操作,我想知道我是否必须担心两次遇到同一个控件。
安全:
public static void Traverse(this Control control, Action<Control> action)
{
Traverse(control, action, new HashSet<control>());
}
private static void Traverse(this Control control, Action<Control> action, HashSet<Control> handled)
{
handled.Add(control);
foreach (Control child in control.Controls)
if (!handled.Contains(child))
Traverse(child, action, handled);
action.Invoke(control);
}
可能不安全:
public static void Traverse(this Control control, Action<Control> action)
{
foreach (Control child in control.Controls)
Traverse(child, action, handled);
action.Invoke(control);
}
散列集是保证这段代码安全所必需的吗?每个控件只需要调用一次动作,不能进入死循环。是不是父子控件的结构这样我就不用担心了?
用法:
this.Traverse(o => o.SuspendLayout());
// Do lots of UI changes
this.Traverse(o => o.ResumeLayout());
执行此操作的(可能)综合方法:
public static class ControlExtensions
{
public static void Traverse(this Control control, Action<Control> action)
{
Traverse(control, action, TraversalMethod.DepthFirst);
}
public static void Traverse(this Control control, Action<Control> action, TraversalMethod method)
{
switch (method)
{
case TraversalMethod.DepthFirst:
TraverseDepth(control, action);
break;
case TraversalMethod.BreadthFirst:
TraverseBreadth(control, action);
break;
case TraversalMethod.ReversedDepthFirst:
TraverseDepthReversed(control, action);
break;
case TraversalMethod.ReversedBreadthFirst:
TraverseBreadthReversed(control, action);
break;
}
}
private static void TraverseDepth(Control control, Action<Control> action)
{
Stack<Control> controls = new Stack<Control>();
Queue<Control> queue = new Queue<Control>();
controls.Push(control);
while (controls.Count != 0)
{
control = controls.Pop();
foreach (Control child in control.Controls)
controls.Push(child);
queue.Enqueue(control);
}
while (queue.Count != 0)
action.Invoke(queue.Dequeue());
}
private static void TraverseBreadth(Control control, Action<Control> action)
{
Queue<Control> controls = new Queue<Control>();
Queue<Control> queue = new Queue<Control>();
controls.Enqueue(control);
while (controls.Count != 0)
{
control = controls.Dequeue();
foreach (Control child in control.Controls)
controls.Enqueue(child);
queue.Enqueue(control);
}
while (queue.Count != 0)
action.Invoke(queue.Dequeue());
}
private static void TraverseDepthReversed(Control control, Action<Control> action)
{
Stack<Control> controls = new Stack<Control>();
Stack<Control> stack = new Stack<Control>();
controls.Push(control);
while (controls.Count != 0)
{
control = controls.Pop();
foreach (Control child in control.Controls)
controls.Push(child);
stack.Push(control);
}
while (stack.Count != 0)
action.Invoke(stack.Pop());
}
private static void TraverseBreadthReversed(Control control, Action<Control> action)
{
Queue<Control> controls = new Queue<Control>();
Stack<Control> stack = new Stack<Control>();
controls.Enqueue(control);
while (controls.Count != 0)
{
control = controls.Dequeue();
foreach (Control child in control.Controls)
controls.Enqueue(child);
stack.Push(control);
}
while (stack.Count != 0)
action.Invoke(stack.Pop());
}
}
【问题讨论】:
-
如果一个孩子有多个父母,那么 Child.Parent 会返回什么?哪个父母会包含孩子?事件/击键会在哪里冒泡?
-
@Rob:所有优点。可以说,可能存在不同类型的遏制,例如一棵树用于事件,另一棵树用于显示(z 顺序等)。在这种情况下,某些控件可能会出现在一个集合中,但不会出现在另一个集合中,而其余控件则同时出现在两个集合中。
-
WPF 实际上在某种程度上做到了这一点。 “控件”的逻辑树和可视树不一定在所有情况下都相同...请参阅:msdn.microsoft.com/en-us/library/ms753391.aspx
-
@Reed:很有趣。如果我理解正确,它看起来像是有效地保留了一棵树,但提供了经过过滤的“逻辑”视图以及完整的“视觉”视图。
-
@Steven:“逻辑”树基本上就是您在代码中设置的内容。但是,由于所有内容都在 WPF 中进行了模板化,因此“可视化树”或实际呈现的内容可能完全不同。树的层之间可以存在更多的“项目”。