逻辑树:
逻辑树描述的是用户界面元素之间的关系,它主要负责:
- 传承依赖属性的值
- 设定动态资源的引用
- 为绑定查询元素的名称
- 传递路由事件
视图树:
视图树包括每一个逻辑元素的模板中的所有视图元素。它的责任是:
- 显示视图元素
- 设定元素的透明度
- 设定元素的布局和渲染变化
- 设定元素的可用(IsEnable)属性
- 做命中测试
- 关联资源(寻根)
Panel类的Childrend属性的类型是UIElementCollection
Panel类暴露了一个类型为UIElement的公有属性“Children”。Panel类重写了VisualChildrenCount属性和GetVisualChild()方法,将Children集合中的成员作为它的可视子元素返回。Panel也使用重写的GetVisualChild()方法为Children集合中的成员提供z-ordering。
public abstract class Panel : FrameworkElement, IAddChild
{
......
private UIElementCollection _uiElementCollection;
......
protected internal UIElementCollection InternalChildren
{
get
{
this.VerifyBoundState();
if (this.IsItemsHost)
{
this.EnsureGenerator();
}
else if (this._uiElementCollection == null)
{
this.EnsureEmptyChildren(this);
}
return this._uiElementCollection;
}
}
......
protected override int VisualChildrenCount
{
get
{
if (this._uiElementCollection == null)
{
return 0;
}
return this._uiElementCollection.Count;
}
}
protected override Visual GetVisualChild(int index)
{
if (this._uiElementCollection == null)
{
throw new ArgumentOutOfRangeException("index", index, SR.Get("Visual_ArgumentOutOfRange"));
}
if (this.IsZStateDirty)
{
this.RecomputeZState();
}
int index2 = (this._zLut != null) ? this._zLut[index] : index;
return this._uiElementCollection[index2];
}
protected virtual UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
{
return new UIElementCollection(this, logicalParent);
}
}
UIElementCollection提供一种非常便捷的方式提供任何元素(可视元素或逻辑元素),不仅仅是面板。
一个元素可以简单地创建一个UIElementCollection的实例并将该元素自己作为集合的可视父元素。然后,任何被添加到集合的UIElement实例都将自动地被视为可视子元素添加到这个元素中。如果在创建集合的时候指定了一个逻辑元素,UIElement实例也会被视为逻辑子元素添加到这个元素中。
UIElementCollection的构造方法如下:
public UIElementCollection(UIElement visualParent, FrameworkElement logicalParent)
Panel类创建UIElementCollection的方法如下:
protected virtual UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
{
return new UIElementCollection(this, logicalParent);
}
从CreateUIElementCollection的实现可以看到,Panel将自身作为集合的可视父元素。因此在将UIElement添加到UIElementCollection中时,UIElementCollection内部会自动将该UIElement元素添加到以Panel作为可视父元素的可视树中,这样子元素就呈现出来了。
protected internal UIElementCollection InternalChildren
{
get
{
this.VerifyBoundState();
if (this.IsItemsHost)
{
this.EnsureGenerator();
}
else if (this._uiElementCollection == null)
{
this.EnsureEmptyChildren(this);
}
return this._uiElementCollection;
}
}
<span style="color: #0000ff; "><strong>internal</strong></span> <span style="color: #ff0000; ">void</span> <span style="color: #191970; "><strong>EnsureGenerator</strong></span>()
{
<span style="color: #0000ff; "><strong>if</strong></span> (<span style="font-weight: bold; ">this</span>._itemContainerGenerator == <span style="font-weight: bold; ">null</span>)
{
<span style="font-weight: bold; ">this</span>.<span style="color: #191970; "><strong>ConnectToGenerator</strong></span>();
<span style="font-weight: bold; ">this</span>.<span style="color: #191970; "><strong>EnsureEmptyChildren</strong></span>(<span style="font-weight: bold; ">null</span>);
<span style="font-weight: bold; ">this</span>.<span style="color: #191970; "><strong>GenerateChildren</strong></span>();
}
}
可以看出如果Panel的IsItemsHost属性为true的话,UIElementCollection的logicalParent将为null。之所以设置为null是因为此时的LogicalParent应该是ItemsControl类或者它的子类型。如果按常规的方式使用Panel,则LogicalParent就是Panel自身,此时LogicalParent和VisualParent相同。
如果Panel的IsItemHost属性为true的话,UIElementCollection会阻止对内部集合的直接修改,只能通过ItemsControl间接地修改。不过UIElementCollection有一个后门允许在IsItemsHost为true的情况下修改内部的集合。这个方法就是通过与ItemsControl关联的ItemContainerGenerator来访问内部集合。
UIElementCollection将成员存储在内部的VisualCollection集合中。
UIElementCollection被创建时会在内部实例化一个VisualCollection类型的集合,这个集合才是真正设置可视父元素和存储Child的地方,同时这个集合还维护者可视父元素与Child之间的关系。UIElementCollection扩展了VisualCollection的功能,增加了维护逻辑父元素和Child之间关系的部分。
UIElementCollection在添加子元素时,如果LogicalParent不为null,会将子元素也添加到逻辑树中
public virtual UIElement this[int index]
{
get
{
return this._visualChildren[index] as UIElement;
}
set
{
this.VerifyWriteAccess();
this.ValidateElement(value);
VisualCollection visualChildren = this._visualChildren;
if (visualChildren[index] != value)
{
UIElement uIElement = visualChildren[index] as UIElement;
if (uIElement != null)
{
this.ClearLogicalParent(uIElement);
}
visualChildren[index] = value;
this.SetLogicalParent(value);
this._visualParent.InvalidateMeasure();
}
}
}
<span style="color: #0000ff; "><strong>protected</strong></span> <span style="color: #ff0000; ">void</span> <span style="color: #191970; "><strong>SetLogicalParent</strong></span>(UIElement element)
{
<span style="color: #0000ff; "><strong>if</strong></span> (<span style="font-weight: bold; ">this</span>._logicalParent != <span style="font-weight: bold; ">null</span>)
{
<span style="font-weight: bold; ">this</span>._logicalParent.<span style="color: #191970; "><strong>AddLogicalChild</strong></span>(element);
}
}
UIElementCollection看上去是可以扩展的,因为它暴露了很多虚方法。此外Panel类也暴露了一个虚方法CreateUIElementCollection,我们可以重写这个方法来创建一个自定义的集合,这个集合必须继承自UIElementCollection。这个自定义集合还必须监听集合的改变,这可以通过重写一些方法:Add,Clear,Insert,Remove,RemoveAt等等。