File: src\Framework\MS\Internal\Documents\UIElementIsland.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
// 
// File: UIElementIsland.cs
//
// Description: UIElement layout island.
//
//---------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;       // List<T>
using System.Collections.ObjectModel;   // ReadOnlyCollection<T>
using System.Windows;                   // UIElement
using System.Windows.Media;             // Visual
 
namespace MS.Internal.Documents
{
    /// <summary> 
    /// UIElement layout island.
    /// </summary>
    internal class UIElementIsland : ContainerVisual, IContentHost, IDisposable
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary> 
        /// Create an instance of a UIElementIsland.
        /// </summary>
        internal UIElementIsland(UIElement child)
        {
            SetFlags(true, VisualFlags.IsLayoutIslandRoot);
            _child = child;
 
            if (_child != null)
            {
                // Disconnect visual from its old parent, if necessary.
                Visual currentParent = VisualTreeHelper.GetParent(_child) as Visual;
                if (currentParent != null)
                {
                    Invariant.Assert(currentParent is UIElementIsland, "Parent should always be a UIElementIsland.");
                    ((UIElementIsland)currentParent).Dispose();
                }
 
                // Notify the Visual layer that a new child appeared.
                Children.Add(_child);
            }
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Internal Methods
        //
        //-------------------------------------------------------------------
 
        #region Internal Methods
 
        /// <summary>
        /// Do layout of entire UIElement Island.
        /// </summary>
        /// <param name="availableSize">Avalilable slot size of the UIElement Island.</param>
        /// <param name="horizontalAutoSize">Whether horizontal autosizing is enabled.</param>
        /// <param name="verticalAutoSize">Whether vertical autosizing is enabled.</param>
        /// <returns>The size of the UIElement Island.</returns>
        internal Size DoLayout(Size availableSize, bool horizontalAutoSize, bool verticalAutoSize)
        {
            Size islandSize = new Size();
            if (_child != null)
            {
                // Get FlowDirection from logical parent and set it on UIElemntIsland
                // to get layout mirroring provided by the layout system.
                if (_child is FrameworkElement && ((FrameworkElement)_child).Parent != null)
                {
                    SetValue(FrameworkElement.FlowDirectionProperty, ((FrameworkElement)_child).Parent.GetValue(FrameworkElement.FlowDirectionProperty));
                }
 
                try
                {
                    _layoutInProgress = true;
 
                    // Measure UIElement
                    _child.Measure(availableSize);
 
                    // Arrange UIElement
                    islandSize.Width = horizontalAutoSize ? _child.DesiredSize.Width : availableSize.Width;
                    islandSize.Height = verticalAutoSize ? _child.DesiredSize.Height : availableSize.Height;
                    _child.Arrange(new Rect(islandSize));
                }
                finally
                {
                    _layoutInProgress = false;
                }
            }
            return islandSize;
        }
 
        #endregion Internal Methods
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// Root of UIElement island.
        /// </summary>
        internal UIElement Root
        {
            get
            {
                return _child;
            }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Internal Events
        //
        //-------------------------------------------------------------------
 
        #region Internal Events
 
        /// <summary>
        /// Fired after DesiredSize for child UIElement has been changed.
        /// </summary>
        internal event DesiredSizeChangedEventHandler DesiredSizeChanged;
 
        #endregion Internal Events
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        private UIElement _child;       // Hosted UIElement root.
        private bool _layoutInProgress; // Whether layout is in progress.
 
        #endregion Private Fields
 
        //-------------------------------------------------------------------
        //
        //  IDisposable Members
        //
        //-------------------------------------------------------------------
 
        #region IDisposable Members
 
        /// <summary>
        /// <see cref="IDisposable.Dispose"/>
        /// </summary>
        public void Dispose()
        {
            if (_child != null)
            {
                Children.Clear();
                _child = null;
            }
            GC.SuppressFinalize(this);
        }
 
        #endregion IDisposable Members
 
        //-------------------------------------------------------------------
        //
        //  IContentHost Members
        //
        //-------------------------------------------------------------------
 
        #region IContentHost Members
 
        /// <summary>
        /// <see cref="IContentHost.InputHitTest"/>
        /// </summary>
        IInputElement IContentHost.InputHitTest(Point point)
        {
            // UIElementIsland only hosts UIElements, which can be found by the
            // normal hit-testing logic, so we don't need to provide our own
            // hit-testing implementation.
            return null;
        }
 
        /// <summary>
        /// <see cref="IContentHost.GetRectangles"/>
        /// </summary>
        ReadOnlyCollection<Rect> IContentHost.GetRectangles(ContentElement child)
        {
            return new ReadOnlyCollection<Rect>(new List<Rect>());
        }
 
        /// <summary>
        /// <see cref="IContentHost.HostedElements"/>
        /// </summary>
        IEnumerator<IInputElement> IContentHost.HostedElements 
        {
            get
            {
                List<IInputElement> hostedElements = new List<IInputElement>();
                if (_child != null)
                {
                    hostedElements.Add(_child);
                }
                return hostedElements.GetEnumerator();
            }
        }
    
        /// <summary>
        /// <see cref="IContentHost.OnChildDesiredSizeChanged"/>
        /// </summary>
        void IContentHost.OnChildDesiredSizeChanged(UIElement child)
        {
            Invariant.Assert(child == _child);
            if (!_layoutInProgress && DesiredSizeChanged != null)
            {
                DesiredSizeChanged(this, new DesiredSizeChangedEventArgs(child));
            }
        }
 
        #endregion IContentHost Members
    }
 
    /// <summary>
    /// DesiredSizeChanged event handler.
    /// </summary>
    internal delegate void DesiredSizeChangedEventHandler(object sender, DesiredSizeChangedEventArgs e);
 
    /// <summary>
    /// Event arguments for the DesiredSizeChanged event.
    /// </summary>
    internal class DesiredSizeChangedEventArgs : EventArgs
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="child">UIElement for which DesiredSize has been changed.</param>
        internal DesiredSizeChangedEventArgs(UIElement child)
        {
            _child = child;
        }
 
        /// <summary>
        /// UIElement for which DesiredSize has been changed.
        /// </summary>
        internal UIElement Child
        {
            get { return _child; }
        }
 
        /// <summary>
        /// UIElement for which DesiredSize has been changed.
        /// </summary>
        private readonly UIElement _child;
    }
}