UIElementCollection

UIElementCollection是WPF中用于描述元素间关系的重要概念,它参与逻辑树与视图树的构建。逻辑树负责依赖属性继承、资源引用、事件传递等,而视图树关注元素的显示、布局、透明度等。UIElementCollection不仅用于Panel的Childrend属性,还能方便地管理和组织元素的可视与逻辑子元素关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

逻辑树:

逻辑树描述的是用户界面元素之间的关系,它主要负责:

  • 传承依赖属性的值
  • 设定动态资源的引用
  • 为绑定查询元素的名称
  • 传递路由事件

视图树:

视图树包括每一个逻辑元素的模板中的所有视图元素。它的责任是:

  • 显示视图元素
  • 设定元素的透明度
  • 设定元素的布局和渲染变化
  • 设定元素的可用(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等等。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值