File: winforms\Managed\System\WinForms\Layout\DockAndAnchorLayout.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="Control.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms.Layout {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Drawing;
 
    using ControlCollection = System.Windows.Forms.Control.ControlCollection;
    using CompModSwitches = System.ComponentModel.CompModSwitches;
 
    internal class DefaultLayout : LayoutEngine {
 
        // Singleton instance shared by all controls.
        internal static readonly DefaultLayout Instance = new DefaultLayout();
 
        private static readonly int _layoutInfoProperty = PropertyStore.CreateKey();
        private static readonly int _cachedBoundsProperty = PropertyStore.CreateKey();
 
        // Loop through the AutoSized controls and expand them if they are smaller than
        // their preferred size.  If expanding the controls causes overlap, bump the overlapped
        // control if it is AutoRelocatable.
        private static void LayoutAutoSizedControls(IArrangedElement container) {
            ArrangedElementCollection children = container.Children;
            for (int i = children.Count - 1; i >= 0; i--) {
                IArrangedElement element = children[i];
            
                if(CommonProperties.xGetAutoSizedAndAnchored(element)) {
                    Rectangle bounds = GetCachedBounds(element);  
                    
                    AnchorStyles anchor = GetAnchor(element);
                    Size proposedConstraints = LayoutUtils.MaxSize;
 
                    if ((anchor & (AnchorStyles.Left | AnchorStyles.Right)) == (AnchorStyles.Left | AnchorStyles.Right)) {
                        proposedConstraints.Width = bounds.Width;
                    }
                    if ((anchor & (AnchorStyles.Top | AnchorStyles.Bottom)) == (AnchorStyles.Top | AnchorStyles.Bottom)) {
                        proposedConstraints.Height = bounds.Height;
                    }
                    Size prefSize = element.GetPreferredSize(proposedConstraints);
            
                    Rectangle newBounds = bounds;
                
                    if (CommonProperties.GetAutoSizeMode(element) == AutoSizeMode.GrowAndShrink) {
                        // this is the case for simple things like radio button, checkbox, etc.
                        newBounds = GetGrowthBounds(element, prefSize);
                    }
                    else {
                        // we had whacked this check, but it turns out it causes undesirable 
                        // behavior in things like panel.  a panel with no elements sizes to 0,0.
                        if(bounds.Width < prefSize.Width || bounds.Height < prefSize.Height) {
                            Size newSize = LayoutUtils.UnionSizes(bounds.Size, prefSize);
                            newBounds = GetGrowthBounds(element, newSize);
                        }
                    }
                    if (newBounds != bounds) {
                         SetCachedBounds(element, newBounds);
                    }
                }             
            }
        }
 
        // Gets the bounds of the element after growing to newSize (note that depending on
        // anchoring the element may grow to the left/updwards rather than to the
        // right/downwards. i.e., it may be translated.)
        private static Rectangle GetGrowthBounds(IArrangedElement element, Size newSize) {
            GrowthDirection direction = GetGrowthDirection(element);
            Rectangle oldBounds = GetCachedBounds(element);
            Point location = oldBounds.Location;
 
            Debug.Assert((CommonProperties.GetAutoSizeMode(element) == AutoSizeMode.GrowAndShrink || newSize.Height >= oldBounds.Height && newSize.Width >= oldBounds.Width),
                "newSize expected to be >= current size.");
 
            if((direction & GrowthDirection.Left) != GrowthDirection.None) {
                // We are growing towards the left, translate X
                location.X -= newSize.Width - oldBounds.Width;
            }
            
            if((direction & GrowthDirection.Upward) != GrowthDirection.None) {
                // We are growing towards the top, translate Y
                location.Y -= newSize.Height - oldBounds.Height;
            }
 
            Rectangle newBounds = new Rectangle(location, newSize);
 
            Debug.Assert((CommonProperties.GetAutoSizeMode(element) == AutoSizeMode.GrowAndShrink || newBounds.Contains(oldBounds)), "How did we resize in such a way we no longer contain our old bounds?");
 
            return newBounds;
        }
        // Examines an elements anchoring to figure out which direction it should grow.
        private static GrowthDirection GetGrowthDirection(IArrangedElement element) {
            AnchorStyles anchor = GetAnchor(element);
            GrowthDirection growthDirection = GrowthDirection.None;
            
            if((anchor & AnchorStyles.Right) != AnchorStyles.None
                && (anchor & AnchorStyles.Left) == AnchorStyles.None) {
                // element is anchored to the right, but not the left.
                growthDirection |= GrowthDirection.Left;
            } else {
                // otherwise we grow towards the right (common case)
                growthDirection |= GrowthDirection.Right;
            }
        
            if((anchor & AnchorStyles.Bottom) != AnchorStyles.None
                && (anchor & AnchorStyles.Top) == AnchorStyles.None) {
                // element is anchored to the bottom, but not the top.
                growthDirection |= GrowthDirection.Upward;
            } else {
                // otherwise we grow towards the bottom. (common case)
                growthDirection |= GrowthDirection.Downward;
            }
 
            Debug.Assert((growthDirection & GrowthDirection.Left) == GrowthDirection.None
                || (growthDirection & GrowthDirection.Right) == GrowthDirection.None,
                "We shouldn't allow growth to both the left and right.");
            Debug.Assert((growthDirection & GrowthDirection.Upward) == GrowthDirection.None
                || (growthDirection & GrowthDirection.Downward) == GrowthDirection.None,
                "We shouldn't allow both upward and downward growth.");
            return growthDirection;
        }
 
        // Layout for a single anchored control.  There's no order dependency when laying out anchored controls.
        private static Rectangle GetAnchorDestination(IArrangedElement element, Rectangle displayRect, bool measureOnly) {
            // Container can not be null since we AschorControls takes a non-null container.
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t'" + element + "' is anchored at " + GetCachedBounds(element).ToString());
 
            AnchorInfo layout = GetAnchorInfo(element);
 
            int left = layout.Left + displayRect.X;
            int top = layout.Top + displayRect.Y;
            int right = layout.Right + displayRect.X;
            int bottom = layout.Bottom + displayRect.Y;
 
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...anchor dim (l,t,r,b) {"
                              + (left)
                              + ", " + (top)
                              + ", " + (right)
                              + ", " + (bottom)
                              + "}");
 
            AnchorStyles anchor = GetAnchor(element);
 
            if (IsAnchored(anchor, AnchorStyles.Right)) {
                Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting right");
                right += displayRect.Width;
 
                if (!IsAnchored(anchor, AnchorStyles.Left)) {
                    Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left");
                    left += displayRect.Width;
                }
            }
            else if (!IsAnchored(anchor, AnchorStyles.Left)) {
                Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left & right");
                right += (displayRect.Width / 2);
                left += (displayRect.Width / 2);
            }
 
            if (IsAnchored(anchor, AnchorStyles.Bottom)) {
                Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting bottom");
                bottom += displayRect.Height;
 
                if (!IsAnchored(anchor, AnchorStyles.Top)) {
                    Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top");
                    top += displayRect.Height;
                }
            }
            else if (!IsAnchored(anchor, AnchorStyles.Top)) {
                Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top & bottom");
                bottom += (displayRect.Height/2);
                top += (displayRect.Height/2);
            }
 
            if (!measureOnly) {
                // the size is actually zero, set the width and heights appropriately.
                if (right < left) right = left;
                if (bottom < top) bottom = top;
            }
            else {
                Rectangle cachedBounds = GetCachedBounds(element);
                // in this scenario we've likely been passed a 0 sized display rectangle to determine our height.
                // we will need to translate the right and bottom edges as necessary to the positive plane.
 
                // right < left means the control is anchored both left and right.
                // cachedBounds != element.Bounds means  the element's size has changed
                // any, all, or none of these can be true.
                if (right < left || cachedBounds.Width != element.Bounds.Width || cachedBounds.X != element.Bounds.X) {
                    if (cachedBounds != element.Bounds) {
                        left = Math.Max(Math.Abs(left), Math.Abs(cachedBounds.Left));
                    }
                    right = left + Math.Max(element.Bounds.Width, cachedBounds.Width) + Math.Abs(right);
                }
                else {
                    left = left > 0 ? left : element.Bounds.Left;
                    right = right > 0 ? right : element.Bounds.Right + Math.Abs(right);
                }
 
                // bottom < top means the control is anchored both top and bottom.
                // cachedBounds != element.Bounds means  the element's size has changed
                // any, all, or none of these can be true.
                if (bottom < top || cachedBounds.Height != element.Bounds.Height || cachedBounds.Y != element.Bounds.Y) {                  
                    if (cachedBounds != element.Bounds) {
                        top = Math.Max(Math.Abs(top), Math.Abs(cachedBounds.Top));
                    }
                    bottom = top + Math.Max(element.Bounds.Height, cachedBounds.Height) + Math.Abs(bottom);
                }
                else {
                    top = top > 0 ? top : element.Bounds.Top;
                    bottom = bottom > 0 ? bottom : element.Bounds.Bottom + Math.Abs(bottom);
                }
            }
                
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...new anchor dim (l,t,r,b) {"
                                                                      + (left)
                                                                      + ", " + (top)
                                                                      + ", " + (right)
                                                                      + ", " + (bottom)
                                                                      + "}");
            
            return new Rectangle(left, top, right - left, bottom - top);
        }
        
        private static void LayoutAnchoredControls(IArrangedElement container) {
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tAnchor Processing");
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\tdisplayRect: " + container.DisplayRectangle.ToString());
 
            Rectangle displayRectangle = container.DisplayRectangle;
            if (CommonProperties.GetAutoSize(container) && ((displayRectangle.Width == 0) || (displayRectangle.Height == 0))) {
                // we havent set oursleves to the preferred size yet.  proceeding will
                // just set all the control widths to zero.  let's return here
                return;
            }
 
 
            ArrangedElementCollection children = container.Children;
            for (int i = children.Count - 1; i >= 0; i--) {
                IArrangedElement element = children[i];
 
                if (CommonProperties.GetNeedsAnchorLayout(element)) {
                    Debug.Assert(GetAnchorInfo(element) != null, "AnchorInfo should be initialized before LayoutAnchorControls().");
                    SetCachedBounds(element, GetAnchorDestination(element, displayRectangle, /*measureOnly=*/false));
                }
            }
        }
 
        private static Size LayoutDockedControls(IArrangedElement container, bool measureOnly) {
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tDock Processing");
            Debug.Assert(!HasCachedBounds(container),
                "Do not call this method with an active cached bounds list.");
 
            // If measuring, we start with an empty rectangle and add as needed.
            // If doing actual layout, we start with the container's rect and subtract as we layout.
            Rectangle remainingBounds = measureOnly ? Rectangle.Empty : container.DisplayRectangle;
            Size preferredSize = Size.Empty;
            
            IArrangedElement mdiClient = null;
 
            // Docking layout is order dependent. After much debate, we decided to use z-order as the
            // docking order.  (Introducing a DockOrder property was a close second)
            ArrangedElementCollection children = container.Children;
            for (int i = children.Count - 1; i >= 0; i--) {
                IArrangedElement element = children[i];
 
                Debug.Assert(element.Bounds == GetCachedBounds(element), "Why do we have cachedBounds for a docked element?");
 
                if (CommonProperties.GetNeedsDockLayout(element)) {
                     // VSWhidbey #109974: Some controls modify their bounds when you call SetBoundsCore.  We
                    // therefore need to read the value of bounds back when adjusting our layout rectangle.
                    switch (GetDock(element)) {
                        case DockStyle.Top: {
                            Size elementSize = GetVerticalDockedSize(element, remainingBounds.Size, measureOnly);
                            Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Y, elementSize.Width, elementSize.Height);
 
                            xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds);
 
                            // What we are really doing here: top += element.Bounds.Height;
                            remainingBounds.Y += element.Bounds.Height;
                            remainingBounds.Height -= element.Bounds.Height;                                
                            break;
                        }
                        case DockStyle.Bottom: {
                            Size elementSize = GetVerticalDockedSize(element, remainingBounds.Size, measureOnly);
                            Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Bottom - elementSize.Height, elementSize.Width, elementSize.Height);
 
                            xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds);
 
                            // What we are really doing here: bottom -= element.Bounds.Height;
                            remainingBounds.Height -= element.Bounds.Height;
                            
                            break;
                        }
                        case DockStyle.Left: {
                            Size elementSize = GetHorizontalDockedSize(element, remainingBounds.Size, measureOnly);
                            Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Y, elementSize.Width, elementSize.Height);
 
                            xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds);
 
                            // What we are really doing here: left += element.Bounds.Width;
                            remainingBounds.X += element.Bounds.Width;
                            remainingBounds.Width -= element.Bounds.Width;
                            break;
                        }
                        case DockStyle.Right: {
                            Size elementSize = GetHorizontalDockedSize(element, remainingBounds.Size, measureOnly);
                            Rectangle newElementBounds = new Rectangle(remainingBounds.Right - elementSize.Width, remainingBounds.Y, elementSize.Width, elementSize.Height);
 
                            xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds);
 
                            // What we are really doing here: right -= element.Bounds.Width;
                            remainingBounds.Width -= element.Bounds.Width;
                            break;
                        }
                        case DockStyle.Fill:
                            if (element is MdiClient) {
                                Debug.Assert(mdiClient == null, "How did we end up with multiple MdiClients?");
                                mdiClient = element;
                            }
                            else {
                                Size elementSize = remainingBounds.Size;//GetFillDockedSize(element, remainingBounds.Size, measureOnly);
                                Rectangle newElementBounds = new Rectangle(remainingBounds.X, remainingBounds.Y, elementSize.Width, elementSize.Height);
 
                                xLayoutDockedControl(element, newElementBounds, measureOnly, ref preferredSize, ref remainingBounds);
                            }
                            break;
                        default:
                            Debug.Fail("Unsupported value for dock.");
                            break;
                    }
                }
 
                // Treat the MDI client specially, since it's supposed to blend in with the parent form
                if (mdiClient != null) {
                    SetCachedBounds(mdiClient, remainingBounds);
                }
            }
            return preferredSize;
        }
 
        // Helper method that either sets the element bounds or does the preferredSize computation based on
        // the value of measureOnly.
        private static void xLayoutDockedControl(IArrangedElement element, Rectangle newElementBounds, bool measureOnly, ref Size preferredSize, ref Rectangle remainingBounds) {
            if (measureOnly) {
                Size neededSize = new Size(
                    Math.Max(0, newElementBounds.Width - remainingBounds.Width),
                    Math.Max(0, newElementBounds.Height - remainingBounds.Height));
 
                DockStyle dockStyle = GetDock(element);
                if ((dockStyle == DockStyle.Top) || (dockStyle == DockStyle.Bottom)) {
                    neededSize.Width = 0;
                }
                if ((dockStyle == DockStyle.Left) || (dockStyle == DockStyle.Right)) {
                    neededSize.Height = 0;
                }
                if (dockStyle != DockStyle.Fill) {
                    preferredSize += neededSize;
                    remainingBounds.Size += neededSize;
                }
                else if(dockStyle == DockStyle.Fill && CommonProperties.GetAutoSize(element)) {
                    Size elementPrefSize = element.GetPreferredSize(neededSize);
                    remainingBounds.Size += elementPrefSize;
                    preferredSize += elementPrefSize;
                }
            } else {
                element.SetBounds(newElementBounds, BoundsSpecified.None);
 
#if DEBUG
                Control control = element as Control;
                newElementBounds.Size = control.ApplySizeConstraints(newElementBounds.Size);
                // This usually happens when a Control overrides its SetBoundsCore or sets size during OnResize
                // to enforce constraints like AutoSize.  Generally you can just move this code to Control.GetAdjustedSize
                // and then PreferredSize will also pick up these constraints.  See ComboBox as an example.
                //
                if (CommonProperties.GetAutoSize(element) && !CommonProperties.GetSelfAutoSizeInDefaultLayout(element)) {
                    Debug.Assert(
                        (newElementBounds.Width < 0 || element.Bounds.Width == newElementBounds.Width) &&
                        (newElementBounds.Height < 0 || element.Bounds.Height == newElementBounds.Height),
                        "Element modified its bounds during docking -- PreferredSize will be wrong. See comment near this assert.");
                }
#endif
            }
        }
 
        private static Size GetVerticalDockedSize(IArrangedElement element, Size remainingSize, bool measureOnly) {
            Size newSize = xGetDockedSize(element, remainingSize, /* constraints = */ new Size(remainingSize.Width, 1), measureOnly);
 
            if (!measureOnly) {
                newSize.Width = remainingSize.Width;
            } else {
                newSize.Width = Math.Max(newSize.Width, remainingSize.Width);
            }
 
            Debug.Assert((measureOnly && (newSize.Width >= remainingSize.Width)) || (newSize.Width == remainingSize.Width),
                "Error detected in GetVerticalDockedSize: Dock size computed incorrectly during layout.");
            
            return newSize;
        }
 
        private static Size GetHorizontalDockedSize(IArrangedElement element, Size remainingSize, bool measureOnly) {
            Size newSize = xGetDockedSize(element, remainingSize, /* constraints = */ new Size(1, remainingSize.Height), measureOnly);
 
            if (!measureOnly) {
                newSize.Height = remainingSize.Height;
            } else {
                newSize.Height = Math.Max(newSize.Height, remainingSize.Height);
            }
 
            Debug.Assert((measureOnly && (newSize.Height >= remainingSize.Height)) || (newSize.Height == remainingSize.Height),
                "Error detected in GetHorizontalDockedSize: Dock size computed incorrectly during layout.");
 
            return newSize;
        }
 
        //Unused
        //private static Size GetFillDockedSize(IArrangedElement element, Size remainingSize, bool measureOnly) {
        //    Size newSize = xGetDockedSize(element, remainingSize, /* constraints = */ remainingSize, measureOnly);
        //    newSize = LayoutUtils.UnionSizes(newSize, remainingSize);
 
        //    if (!measureOnly) {
        //        newSize = LayoutUtils.IntersectSizes(newSize, remainingSize);
        //    }
 
        //    Debug.Assert(newSize.Width >= remainingSize.Width && newSize.Height >= remainingSize.Height,
        //        "Error detected in GetFillDockedSize: Element did not stretch to remaning size.");
        //    Debug.Assert(measureOnly || (newSize.Width == remainingSize.Width && newSize.Height == remainingSize.Height),
        //        "Error detected in GetFillDockedSize: Dock size computed incorrectly during layout.");
 
        //    return newSize;
        //}
 
        private static Size xGetDockedSize(IArrangedElement element, Size remainingSize, Size constraints, bool measureOnly) {
            Size desiredSize;
            
            if (CommonProperties.GetAutoSize(element)) {
                // Ask control for its desired size using the provided constraints.
                // (e.g., a control docked to top will constrain width to remaining width
                // and minimize height.)
                desiredSize = element.GetPreferredSize(constraints);
            } else {
                desiredSize = element.Bounds.Size;
            }
 
            Debug.Assert((desiredSize.Width >= 0 && desiredSize.Height >= 0), "Error detected in xGetDockSize: Element size was negative.");
            return desiredSize;
        }
 
        internal override bool LayoutCore(IArrangedElement container, LayoutEventArgs args) {
            Size garbage;     // PreferredSize is garbage if we are not measuring.
            return xLayout(container, /* measureOnly = */ false, out garbage);
        }
 
        // Note: PreferredSize is only computed if measureOnly = true.
        private static bool xLayout(IArrangedElement container, bool measureOnly, out Size preferredSize) {
        /*
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, 
                              string.Format("{1}(hwnd=0x{0:X}).OnLayout", 
                                            (container.IsHandleCreated ? container.Handle : IntPtr.Zero), 
                                            container.GetType().FullName));
        */
            
            ArrangedElementCollection children = container.Children;
            preferredSize = new Size(-7103, -7105);     // PreferredSize is garbage unless measureOnly is specified
            
            // short circuit for items with no children
            if (!measureOnly && children.Count == 0) {
                return CommonProperties.GetAutoSize(container);
            }
 
            bool dock = false;
            bool anchor = false;
            bool autoSize = false;
 
      
            for (int i = children.Count - 1; i >= 0; i--) {
                IArrangedElement element = children[i];
                
                if (CommonProperties.GetNeedsDockAndAnchorLayout(element)) {
                    if (!dock && CommonProperties.GetNeedsDockLayout(element)) dock = true;
                    if (!anchor && CommonProperties.GetNeedsAnchorLayout(element)) anchor = true;
                    if (!autoSize && CommonProperties.xGetAutoSizedAndAnchored(element)) autoSize = true;
                }
            }
 
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tanchor : " + anchor.ToString());
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\tdock :   " + dock.ToString());
 
            Size preferredSizeForDocking = Size.Empty;
            Size preferredSizeForAnchoring = Size.Empty;
 
            if (dock) preferredSizeForDocking = LayoutDockedControls(container, measureOnly);
            if (anchor && !measureOnly) { 
                // in the case of anchor, where we currently are defines the preferred size,
                // so dont recalculate the positions of everything.
                LayoutAnchoredControls(container);
            }
         
            if (autoSize) LayoutAutoSizedControls(container);
 
            if (!measureOnly) {
                // Set the anchored controls to their computed positions.
                ApplyCachedBounds(container);             
            }
            else {
      
                // Finish the preferredSize computation and clear cached anchored positions.
                preferredSizeForAnchoring = GetAnchorPreferredSize(container);
 
                Padding containerPadding = Padding.Empty;
                Control control = container as Control;
                if (control != null) {
                   // calling this will respect Control.DefaultPadding.
                   containerPadding = control.Padding;
                }
                else { // not likely to happen but…
                   containerPadding = CommonProperties.GetPadding(container, Padding.Empty);
                }
 
                preferredSizeForAnchoring.Width -= containerPadding.Left;
                preferredSizeForAnchoring.Height -= containerPadding.Top;
 
                ClearCachedBounds(container);
                preferredSize = LayoutUtils.UnionSizes(preferredSizeForDocking, preferredSizeForAnchoring); 
 
            }
            return CommonProperties.GetAutoSize(container);
        }
 
        /// <devdoc>
        ///     Updates the Anchor information based on the controls current bounds.
        ///     This should only be called when the parent control changes or the
        ///     anchor mode changes.
        /// </devdoc>
        private static void UpdateAnchorInfo(IArrangedElement element) {
            Debug.Assert(!HasCachedBounds(element.Container),
                "Do not call this method with an active cached bounds list.");
 
            AnchorInfo anchorInfo = GetAnchorInfo(element);
            if(anchorInfo == null) {
                anchorInfo = new AnchorInfo();
                SetAnchorInfo(element, anchorInfo);
            }
            
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "Update anchor info");
            Debug.Indent();
            Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, element.Container == null ? "No parent" : "Parent");
 
 
            if (CommonProperties.GetNeedsAnchorLayout(element) && element.Container != null) {
 
                Rectangle bounds = GetCachedBounds(element);
                
                AnchorInfo oldAnchorInfo = new AnchorInfo();
                oldAnchorInfo.Left = anchorInfo.Left;
                oldAnchorInfo.Top = anchorInfo.Top;
                oldAnchorInfo.Right = anchorInfo.Right;
                oldAnchorInfo.Bottom = anchorInfo.Bottom;
 
                anchorInfo.Left = element.Bounds.Left;
                anchorInfo.Top = element.Bounds.Top;
                anchorInfo.Right = element.Bounds.Right;
                anchorInfo.Bottom = element.Bounds.Bottom;
 
                Rectangle parentDisplayRect = element.Container.DisplayRectangle;
                Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "Parent displayRectangle" + parentDisplayRect);
                int parentWidth = parentDisplayRect.Width;
                int parentHeight = parentDisplayRect.Height;
 
                // VS#46140
                // The anchor is relative to the parent DisplayRectangle, so offset the anchor
                // by the DisplayRect origin
                anchorInfo.Left -= parentDisplayRect.X;
                anchorInfo.Top -= parentDisplayRect.Y;
                anchorInfo.Right -= parentDisplayRect.X;
                anchorInfo.Bottom -= parentDisplayRect.Y;
 
                AnchorStyles anchor = GetAnchor(element);
                if (IsAnchored(anchor, AnchorStyles.Right)) {
                    if (DpiHelper.EnableAnchorLayoutHighDpiImprovements && (anchorInfo.Right - parentWidth > 0) && (oldAnchorInfo.Right < 0)) {
                        // parent was resized to fit its parent, or screen, we need to reuse old anchor info to prevent losing control beyond right edge
                        anchorInfo.Right = oldAnchorInfo.Right;
                        anchorInfo.Left = oldAnchorInfo.Right - bounds.Width;   // control might have been resized, update Left anchor
                    }
                    else {
                        anchorInfo.Right -= parentWidth;
 
                        if (!IsAnchored(anchor, AnchorStyles.Left)) {
                            anchorInfo.Left -= parentWidth;
                        }
                    }
                }
                else if (!IsAnchored(anchor, AnchorStyles.Left)) {
                    anchorInfo.Right -= (parentWidth/2);
                    anchorInfo.Left -= (parentWidth/2);
                }
 
                if (IsAnchored(anchor, AnchorStyles.Bottom)) {
                    if (DpiHelper.EnableAnchorLayoutHighDpiImprovements && (anchorInfo.Bottom - parentHeight > 0) && (oldAnchorInfo.Bottom < 0)) {
                        // parent was resized to fit its parent, or screen, we need to reuse old anchor info to prevent losing control beyond bottom edge
                        anchorInfo.Bottom = oldAnchorInfo.Bottom;
                        anchorInfo.Top = oldAnchorInfo.Bottom - bounds.Height;   // control might have been resized, update Top anchor
                    }
                    else {
                        anchorInfo.Bottom -= parentHeight;
 
                        if (!IsAnchored(anchor, AnchorStyles.Top)) {
                            anchorInfo.Top -= parentHeight;
                        }
                    }
                }
                else if (!IsAnchored(anchor, AnchorStyles.Top)) {
                    anchorInfo.Bottom -= (parentHeight/2);
                    anchorInfo.Top -= (parentHeight/2);
                }
                Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "anchor info (l,t,r,b): (" + anchorInfo.Left + ", " + anchorInfo.Top  + ", " + anchorInfo.Right  + ", " + anchorInfo.Bottom + ")");
            }
            Debug.Unindent();
        }
 
        public static AnchorStyles GetAnchor(IArrangedElement element) {
            return CommonProperties.xGetAnchor(element);
        }
        
        public static void SetAnchor(IArrangedElement container, IArrangedElement element, AnchorStyles value) {
            AnchorStyles oldValue = GetAnchor(element);
            if (oldValue != value) {
                if(CommonProperties.GetNeedsDockLayout(element)) {
                    // We set dock back to none to cause the element to size back to its original bounds.
                    SetDock(element, DockStyle.None);
                }
 
                CommonProperties.xSetAnchor(element, value);
 
                if (CommonProperties.GetNeedsAnchorLayout(element)) {
                    UpdateAnchorInfo(element);
                }
                else {
                    SetAnchorInfo(element, null);
                }
 
                if (element.Container != null) {
                    bool rightReleased = IsAnchored(oldValue, AnchorStyles.Right) && !IsAnchored(value, AnchorStyles.Right);
                    bool bottomReleased = IsAnchored(oldValue, AnchorStyles.Bottom) && !IsAnchored(value, AnchorStyles.Bottom);
                    if(element.Container.Container != null && (rightReleased || bottomReleased)) {
                        // If the right or bottom anchor is being released, we have a special case where the element's
                        // margin may affect preferredSize where it didn't previously.  Rather than do an expensive
                        // check for this in OnLayout, we just detect the case her and force a relayout.
                        LayoutTransaction.DoLayout(element.Container.Container, element, PropertyNames.Anchor);
                    }
                    LayoutTransaction.DoLayout(element.Container, element, PropertyNames.Anchor);
                }
            }
            Debug.Assert(GetAnchor(element) == value, "Error setting Anchor value.");
        }
 
        public static DockStyle GetDock(IArrangedElement element) {
            return CommonProperties.xGetDock(element);
        }
 
        public static void SetDock(IArrangedElement element, DockStyle value) {
            Debug.Assert(!HasCachedBounds(element.Container),
                "Do not call this method with an active cached bounds list.");
 
            if (GetDock(element) != value) {
                //valid values are 0x0 to 0x5
                if (!ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill)){
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(DockStyle));
                }
 
                bool dockNeedsLayout = CommonProperties.GetNeedsDockLayout(element);
                CommonProperties.xSetDock(element, value);
 
                using (new LayoutTransaction(element.Container as Control, element, PropertyNames.Dock)) {   
                    // VSWHDIBEY 227715 if the item is autosized, calling setbounds performs a layout, which
                    // if we havent set the anchor info properly yet makes dock/anchor layout cranky.
                    
                    if (value == DockStyle.None) {
                        if (dockNeedsLayout) {
                            // We are transitioning from docked to not docked, restore the original bounds.
                            element.SetBounds(CommonProperties.GetSpecifiedBounds(element), BoundsSpecified.None);
                            // VSWHIDBEY 159532 restore Anchor information as its now relevant again.
                            UpdateAnchorInfo(element);
                        }
                    }
                    else {
                        // Now setup the new bounds.
                        //
                        element.SetBounds(CommonProperties.GetSpecifiedBounds(element), BoundsSpecified.All);
                    }
                }
             
            }
            Debug.Assert(GetDock(element) == value, "Error setting Dock value.");
        }
 
        public static void ScaleAnchorInfo(IArrangedElement element, SizeF factor) {
            AnchorInfo anchorInfo = GetAnchorInfo(element);
 
            // some controls don't have AnchorInfo, i.e. Panels
            if (anchorInfo != null) {
                anchorInfo.Left = (int)((float)anchorInfo.Left * factor.Width);
                anchorInfo.Top = (int)((float)anchorInfo.Top * factor.Height);
                anchorInfo.Right = (int)((float)anchorInfo.Right * factor.Width);
                anchorInfo.Bottom = (int)((float)anchorInfo.Bottom * factor.Height);
 
                SetAnchorInfo(element, anchorInfo);
            }
        }
 
        private static Rectangle GetCachedBounds(IArrangedElement element) {
            if(element.Container != null) {
                IDictionary dictionary = (IDictionary) element.Container.Properties.GetObject(_cachedBoundsProperty);
                if(dictionary != null) {
                    object bounds = dictionary[element];
                    if(bounds != null) {
                        return (Rectangle) bounds;
                    }
                }
            }
            return element.Bounds;
        }
 
        private static bool HasCachedBounds(IArrangedElement container) {
            return container != null && container.Properties.GetObject(_cachedBoundsProperty) != null;
        }
 
        private static void ApplyCachedBounds(IArrangedElement container) {
            if (CommonProperties.GetAutoSize(container)) {
                //Avoiding calling DisplayRectangle before checking AutoSize for Everett compat (VSWhidbey 421433)
                Rectangle displayRectangle = container.DisplayRectangle;
                if ((displayRectangle.Width == 0) || (displayRectangle.Height == 0)) {
                    ClearCachedBounds(container);
                    return;
                }
            }
 
 
 
            IDictionary dictionary = (IDictionary) container.Properties.GetObject(_cachedBoundsProperty);
            if(dictionary != null) {
#if DEBUG
                // In debug builds, we need to modify the collection, so we add a break and an
                // outer loop to prevent attempting to IEnumerator.MoveNext() on a modified
                // collection.
                while(dictionary.Count > 0) {
#endif
                foreach(DictionaryEntry entry in dictionary) {
                    IArrangedElement element = (IArrangedElement) entry.Key;
 
                    Debug.Assert(element.Container == container,"We have non-children in our containers cached bounds store.");
#if DEBUG
                    // We are about to set the bounds to the cached value.  We clear the cached value
                    // before SetBounds because some controls fiddle with the bounds on SetBounds
                    // and will callback InitLayout with a different bounds and BoundsSpecified.
                    dictionary.Remove(entry.Key);
#endif
                    Rectangle bounds = (Rectangle) entry.Value;
                    element.SetBounds(bounds, BoundsSpecified.None);
#if DEBUG
                    break;
                }
#endif
                }
                ClearCachedBounds(container);
            }
        }
 
        private static void ClearCachedBounds(IArrangedElement container) {
            container.Properties.SetObject(_cachedBoundsProperty, null);
        }
 
        private static void SetCachedBounds(IArrangedElement element, Rectangle bounds) {
            if(bounds != GetCachedBounds(element)) {
                IDictionary dictionary = (IDictionary) element.Container.Properties.GetObject(_cachedBoundsProperty);
                if (dictionary == null) {
                    dictionary = new HybridDictionary();
                    element.Container.Properties.SetObject(_cachedBoundsProperty, dictionary);
                }
                dictionary[element] = bounds;
            }
        }
 
        private static AnchorInfo GetAnchorInfo(IArrangedElement element) {
            return (AnchorInfo) element.Properties.GetObject(_layoutInfoProperty);
        }
 
        private static void SetAnchorInfo(IArrangedElement element, AnchorInfo value) {
            element.Properties.SetObject(_layoutInfoProperty, value);
        }
 
        internal override void InitLayoutCore(IArrangedElement element, BoundsSpecified specified) {
            Debug.Assert(specified == BoundsSpecified.None || GetCachedBounds(element) == element.Bounds,
                "Attempt to InitLayout while element has active cached bounds.");
 
            if(specified != BoundsSpecified.None && CommonProperties.GetNeedsAnchorLayout(element)) {
                UpdateAnchorInfo(element);
            }
        }
 
        internal override Size GetPreferredSize(IArrangedElement container, Size proposedBounds) {
            Debug.Assert(!HasCachedBounds(container),
                "Do not call this method with an active cached bounds list.");
            
            Size prefSize;
            
            xLayout(container, /* measureOnly = */ true, out prefSize);
/*            container.Bounds = prefSize;
            xLayout(container, false, out prefSize);
 
            // make sure controls are big enough to fit on form if not - increase container size
            container.Bounds = newBounds;
 
            xLayout(container, true, out prefSize);*/
            
            return prefSize;
        }
 
        private static Size GetAnchorPreferredSize(IArrangedElement container) {
            Size prefSize = Size.Empty;
                     
            ArrangedElementCollection children = container.Children;
            for (int i = children.Count - 1; i >= 0; i--) {                    
                IArrangedElement element = container.Children[i];
                if(!CommonProperties.GetNeedsDockLayout(element) && element.ParticipatesInLayout) {
                    AnchorStyles anchor = GetAnchor(element);
                    Padding margin = CommonProperties.GetMargin(element);
                    Rectangle elementSpace = LayoutUtils.InflateRect(GetCachedBounds(element), margin);
 
                    if (IsAnchored(anchor, AnchorStyles.Left) && !IsAnchored(anchor, AnchorStyles.Right)) {
                        // If we are anchored to the left we make sure the container is large enough not to clip us
                        // (unless we are right anchored, in which case growing the container will just resize us.)
                        prefSize.Width = Math.Max(prefSize.Width, elementSpace.Right);
                    }
                    
                    if (/*IsAnchored(anchor, AnchorStyles.Top) &&*/ !IsAnchored(anchor, AnchorStyles.Bottom)) {
                        // If we are anchored to the top we make sure the container is large enough not to clip us
                        // (unless we are bottom anchored, in which case growing the container will just resize us.)
                        prefSize.Height = Math.Max(prefSize.Height, elementSpace.Bottom);
                    
                    }
 
                    if (IsAnchored(anchor, AnchorStyles.Right)) {
                        // If we are right anchored, see what the anchor distance between our right edge and
                        // the container is, and make sure our container is large enough to accomodate us.                            
                        Rectangle anchorDest = GetAnchorDestination(element, Rectangle.Empty, /*measureOnly=*/true);
                        if (anchorDest.Width < 0) {
                            prefSize.Width = Math.Max(prefSize.Width, elementSpace.Right + anchorDest.Width);
                        }
                        else {
                            prefSize.Width = Math.Max(prefSize.Width, anchorDest.Right);
                        }
                    }
                    
                    if (IsAnchored(anchor, AnchorStyles.Bottom)) { 
                        // If we are right anchored, see what the anchor distance between our right edge and
                        // the container is, and make sure our container is large enough to accomodate us.                            
                        Rectangle anchorDest = GetAnchorDestination(element, Rectangle.Empty, /*measureOnly=*/true);
                        if (anchorDest.Height < 0) {
                            prefSize.Height = Math.Max(prefSize.Height, elementSpace.Bottom + anchorDest.Height);
                        }
                        else {
                            prefSize.Height = Math.Max(prefSize.Height, anchorDest.Bottom);
                        }
                    }
 
 
                }
            }     
            return prefSize;
        }
 
        public static bool IsAnchored(AnchorStyles anchor, AnchorStyles desiredAnchor) {
            return (anchor & desiredAnchor) == desiredAnchor;
        }
 
        [Flags]
        private enum GrowthDirection {
            None        = 0,
            Upward      = 0x01,
            Downward    = 0x02,
            Left        = 0x04,
            Right       = 0x08
        }
 
 
#if DEBUG_PAINT_ANCHOR
	// handy method for drawing out the child anchor infos
        internal static void  DebugPaintAnchor(Graphics g, Control parent) {
            foreach (Control child in parent.Controls) {
 
                
                AnchorInfo layout = GetAnchorInfo(child as IArrangedElement);
                Rectangle displayRect = parent.DisplayRectangle;
 
                if (layout == null) {
                    continue;
                }
                int left = layout.Left + displayRect.X;
                int top = layout.Top + displayRect.Y;
                int right = layout.Right + displayRect.X;
                int bottom = layout.Bottom + displayRect.Y;
     
                AnchorStyles anchor = GetAnchor(child as IArrangedElement);
 
                // Repeat of GetAnchorDestination
                if (IsAnchored(anchor, AnchorStyles.Right)) {
                    Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting right");
                    right += displayRect.Width;
     
                    if (!IsAnchored(anchor, AnchorStyles.Left)) {
                        Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left");
                        left += displayRect.Width;
                    }
                }
                else if (!IsAnchored(anchor, AnchorStyles.Left)) {
                    Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting left & right");
                    right += (displayRect.Width / 2);
                    left += (displayRect.Width / 2);
                }
     
                if (IsAnchored(anchor, AnchorStyles.Bottom)) {
                    Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting bottom");
                    bottom += displayRect.Height;
     
                    if (!IsAnchored(anchor, AnchorStyles.Top)) {
                        Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top");
                        top += displayRect.Height;
                    }
                }
                else if (!IsAnchored(anchor, AnchorStyles.Top)) {
                    Debug.WriteLineIf(CompModSwitches.RichLayout.TraceInfo, "\t\t...adjusting top & bottom");
                    bottom += (displayRect.Height/2);
                    top += (displayRect.Height/2);
                }
 
                if (IsAnchored(anchor, AnchorStyles.Right)) {
                    TextRenderer.DrawText(g, "right " + layout.Right.ToString(), parent.Font, new Point(right/2, child.Top - 20), Color.HotPink);
                    g.FillRectangle(Brushes.HotPink, 0,  child.Top -4, right, 1);
                }
                if (IsAnchored(anchor, AnchorStyles.Left)) {
                    TextRenderer.DrawText(g, "left " + layout.Left.ToString(), parent.Font, new Point(left/2, child.Top - 16), Color.Green);
                    g.FillRectangle(Brushes.Green, 0, child.Top -2, left, 1);                    
                }                          
                if (IsAnchored(anchor, AnchorStyles.Top)) {
                    TextRenderer.DrawText(g, "top " + layout.Top.ToString(), parent.Font, new Point(child.Left -100, top/2), Color.Blue);
 
                    g.FillRectangle(Brushes.Blue, child.Left -1, 0, 1, top);                    
                }
                if (IsAnchored(anchor, AnchorStyles.Bottom)) {
                    TextRenderer.DrawText(g, "bottom " + layout.Bottom.ToString(), parent.Font, new Point(child.Left -100, right/2), Color.Red);
                    g.FillRectangle(Brushes.Red, child.Left -2, 1, 1, bottom);                    
                }
            }
        }
#endif
 
#if DEBUG_LAYOUT
#if DEBUG

        internal static string Debug_GetAllLayoutInformation(Control start, int indents) {
             string info = Debug_GetLayoutInfo(start, indents) + "\r\n";
     
             for (int i = 0; i < start.Controls.Count; i++) {
                 info += Debug_GetIndents(indents) + "+-->" + Debug_GetAllLayoutInformation(start.Controls[i], indents + 1) + "\r\n";
             }
             return info;
         }
         internal static string Debug_GetIndents(int indents) {
             string str = "";
             for (int i = 0; i < indents; i++) {
                 str += "\t";
             }
             return str;
         }
         internal static string Debug_GetLayoutInfo(Control control, int indents) {
             string lineBreak = "\r\n" + Debug_GetIndents(indents + 1);
             string layoutInfo = string.Format(System.Globalization.CultureInfo.CurrentCulture, 
                                                "Handle {9} Name {1} Type {2} {0} Bounds {3} {0} AutoSize {4} {0} Dock [{5}] Anchor [{6}] {0} Padding [{7}] Margin [{8}]",
                                                 lineBreak,
                                                 control.Name,
                                                 control.GetType().Name,
                                                 control.Bounds,
                                                 control.AutoSize,
                                                 control.Dock,
                                                 control.Anchor,
                                                 control.Padding,
                                                 control.Margin,
                                                 !control.IsHandleCreated ? "[not created]" : "0x" + ((int)(control.Handle)).ToString("x"));
             if (control is TableLayoutPanel) {
                 TypeConverter converter = TypeDescriptor.GetConverter(typeof(TableLayoutSettings));
     
                 layoutInfo += lineBreak + converter.ConvertTo(control as TableLayoutPanel, typeof(string));
             }
             return layoutInfo;
     
         }
#endif
#endif    
        
        #region AnchorInfo
        private sealed class AnchorInfo {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;           
        }        
        #endregion AnchorInfo
    }
}