Reference Sorceを読む。 Panel - 2Page

Reference Sorceを勉強ついでに読み込んでいきます。
個人的な備忘録を兼ねた記事となります。

引き続きPanelの読み込みです。
まずは主要素であるChildrenを構成を確認します。

public UIElementCollection Children

        /// <summary>
        /// Returns a UIElementCollection of children for user to add/remove children manually
        /// Returns read-only collection if Panel is data-bound (no manual control of children is possible,
        /// the associated ItemsControl completely overrides children)
        /// Note: the derived Panel classes should never use this collection for
        /// internal purposes like in their MeasureOverride or ArrangeOverride.
        /// They should use InternalChildren instead, because InternalChildren
        /// is always present and either is a mirror of public Children collection (in case of Direct Panel)
        /// or is generated from data binding.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public UIElementCollection Children
        {
            get
            {
                //When we will change from UIElementCollection to IList<UIElement>, we might
                //consider returning a wrapper IList here which coudl be read-only for mutating methods
                //while INternalChildren could be R/W even in case of Generator attached.
                return InternalChildren;
            }
        }

[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

この属性は列挙でVisible,Content,HIddenがあります。
MSDNを参照すると下記が表で記載されています。

https://msdn.microsoft.com/ja-jp/library/system.componentmodel.designerserializationvisibility(v=vs.110).aspx

メンバー名 説明
Content
コード ジェネレーターは、オブジェクト自体ではなく、オブジェクトの内容のコードを生成します。
Hidden
コード ジェネレーターは、オブジェクトのコードを生成しません。
Visible
コード ジェネレーターは、オブジェクトのコードを生成します。

Content とVisibleがわかりにくいですがシンプルな値であればVisibleを指定します(デフォルトはこれ)
ただし、TextBoxのFontなど複雑なクラスの情報を保持するものはContentを指定する必要があります。

protected internal UIElementCollection InternalChildren

Childrenプロパティの戻り値で指定しているInternalChildrenです。 呼び出しているメソッドも含めて確認します。

        /// <summary>
        /// Returns a UIElementCollection of children - added by user or generated from data binding.
        /// Panel-derived classes should use this collection for all internal purposes, including
        /// MeasureOverride/ArrangeOverride overrides.
        /// </summary>
        protected internal UIElementCollection InternalChildren
        {
            get
            {
                VerifyBoundState();
 
                if (IsItemsHost)
                {
                    EnsureGenerator();
                }
                else
                {
                    if (_uiElementCollection == null)
                    {
                        // First access on a regular panel
                        EnsureEmptyChildren(/* logicalParent = */ this);
                    }
                }
 
                return _uiElementCollection;
            }
        }

private bool VerifyBoundState()

アンバウンドになっていないかを確認し必要に応じて処理を行っています。 スタイルの切り替えやテーマの変更などによって不適切な状態になっていれば最適化するための処理を行っています。 まずはItemsHostの状態を維持できているかを確認し ItemsHostではないのにItemsのジェネレータがあるとクリアしています。 ItemsHostである場合にジェネレータがNullの場合、ClearChildrenメソッドにて状態をリセットしています。

       private bool VerifyBoundState()
        {
            // If the panel becomes "unbound" while attached to a generator, this
            // method detaches it and makes it really behave like "unbound."  This
            // can happen because of a style change, a theme change, etc. It returns
            // the correct "bound" state, after the dust has settled.
            //
            // This is really a workaround for a more general problem that the panel
            // needs to release resources (an event handler) when it is "out of the tree."
            // Currently, there is no good notification for when this happens.
 
            bool isItemsHost = (ItemsControl.GetItemsOwnerInternal(this) != null);
 
            if (isItemsHost)
            {
                if (_itemContainerGenerator == null)
                {
                    // Transitioning from being unbound to bound
                    ClearChildren();
                }
 
                return (_itemContainerGenerator != null);
            }
            else
            {
                if (_itemContainerGenerator != null)
                {
                    // Transitioning from being bound to unbound
                    DisconnectFromGenerator();
                    ClearChildren();
                }
 
                return false;
            }
        }

EnsureGenerator

InternalChildren取得時にIsItemsHost=trueだとコールされます。 直訳すると"確実に生成する” アイテムコンテナーのジェネレータがNullの場合は再生成します。

        internal void EnsureGenerator()
        {
            Debug.Assert(IsItemsHost, "Should be invoked only on an ItemsHost panel");
 
            if (_itemContainerGenerator == null)
            {
                // First access on an items presenter panel
                ConnectToGenerator();
 
                // Children of this panel should not have their logical parent reset
                EnsureEmptyChildren(/* logicalParent = */ null);
 
                GenerateChildren();
            }
        }

EnsureEmptyChildren

IsItemsHost取得時にIsItemsHost=falseだとコールされます。 uiElementCollectionが空、もしくは論理的な親が自分はない場合にuiElementCollectionを再生成しています。

        private void EnsureEmptyChildren(FrameworkElement logicalParent)
        {
            if ((_uiElementCollection == null) || (_uiElementCollection.LogicalParent != logicalParent))
            {
                _uiElementCollection = CreateUIElementCollection(logicalParent);
            }
            else
            {
                ClearChildren();
            }
        }

今日はここまで。