LayoutUtils.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / WinForms / Managed / System / WinForms / Layout / LayoutUtils.cs / 1 / LayoutUtils.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Windows.Forms.Layout { 
    using System; 
    using System.Collections;
    using System.Diagnostics; 
    using System.Drawing;
    using System.Windows.Forms.Internal;
    using System.Drawing.Text;
    using System.Runtime.InteropServices; 
    using System.Windows.Forms;
    using System.Windows.Forms.ComponentModel; 
    using System.Collections.Generic; 

    // Utilities used by layout code.  If you use these outside of the layout 
    // namespace, you should probably move them to WindowsFormsUtils.
    internal class LayoutUtils {

        public static readonly Size MaxSize = new Size(Int32.MaxValue, Int32.MaxValue); 
        public static readonly Size InvalidSize = new Size(Int32.MinValue, Int32.MinValue);
 
        public static readonly Rectangle MaxRectangle = new Rectangle(0, 0, Int32.MaxValue, Int32.MaxValue); 

        public const ContentAlignment AnyTop = ContentAlignment.TopLeft | ContentAlignment.TopCenter | ContentAlignment.TopRight; 
        public const ContentAlignment AnyBottom = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight;
        public const ContentAlignment AnyLeft = ContentAlignment.TopLeft | ContentAlignment.MiddleLeft | ContentAlignment.BottomLeft;
        public const ContentAlignment AnyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight;
        public const ContentAlignment AnyCenter = ContentAlignment.TopCenter | ContentAlignment.MiddleCenter | ContentAlignment.BottomCenter; 
        public const ContentAlignment AnyMiddle = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight;
 
        public const AnchorStyles HorizontalAnchorStyles = AnchorStyles.Left | AnchorStyles.Right; 
        public const AnchorStyles VerticalAnchorStyles   = AnchorStyles.Top | AnchorStyles.Bottom;
 
        private static readonly AnchorStyles[] dockingToAnchor = new AnchorStyles[] {
            /* None   */ AnchorStyles.Top | AnchorStyles.Left,
            /* Top    */ AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
            /* Bottom */ AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, 
            /* Left   */ AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom,
            /* Right  */ AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom, 
            /* Fill   */ AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left 
        };
 
        // A good, short test string for measuring control height.
        public readonly static string TestString = "j^";

 
        // Returns the size of the largest string in the given collection. Non-string objects are converted
        // with ToString(). Uses OldMeasureString, not GDI+. Does not support multiline. 
        public static Size OldGetLargestStringSizeInCollection(Font font, ICollection objects) { 
            Size largestSize = Size.Empty;
            if (objects != null) { 
                foreach(object obj in objects) {
                    Size textSize = TextRenderer.MeasureText(obj.ToString(), font, new Size(Int16.MaxValue, Int16.MaxValue), TextFormatFlags.SingleLine);
                    largestSize.Width = Math.Max(largestSize.Width, textSize.Width);
                    largestSize.Height = Math.Max(largestSize.Height, textSize.Height); 
                }
            } 
            return largestSize; 
        }
 


        /*
         *  We can cut ContentAlignment from a max index of 1024 (12b) down to 11 (4b) through 
         *  bit twiddling.  The int result of this function maps to the ContentAlignment as indicated
         *  by the table below: 
         * 
         *          Left      Center    Right
         *  Top     0000 0x0  0001 0x1  0010 0x2 
         *  Middle  0100 0x4  0101 0x5  0110 0x6
         *  Bottom  1000 0x8  1001 0x9  1010 0xA
         *
         *  (The high 2 bits determine T/M/B.  The low 2 bits determine L/C/R.) 
         */
 
        public static int ContentAlignmentToIndex(ContentAlignment alignment) { 
            /*
             *  Here is what content alignment looks like coming in: 
             *
             *          Left    Center  Right
             *  Top     0x001   0x002   0x004
             *  Middle  0x010   0x020   0x040 
             *  Bottom  0x100   0x200   0x400
             * 
             *  (L/C/R determined bit 1,2,4.  T/M/B determined by 4 bit shift.) 
             */
 
            int topBits = xContentAlignmentToIndex(((int)alignment) & 0x0F);
            int middleBits = xContentAlignmentToIndex(((int)alignment >> 4) & 0x0F);
            int bottomBits = xContentAlignmentToIndex(((int)alignment >> 8) & 0x0F);
 
            Debug.Assert((topBits != 0 && (middleBits == 0 && bottomBits == 0))
                || (middleBits != 0 && (topBits == 0 && bottomBits == 0)) 
                || (bottomBits != 0 && (topBits == 0 && middleBits == 0)), 
                "One (and only one) of topBits, middleBits, or bottomBits should be non-zero.");
 
            int result = (middleBits != 0 ? 0x04 : 0) | (bottomBits != 0 ? 0x08 : 0) | topBits | middleBits | bottomBits;

            // zero isn't used, so we can subtract 1 and start with index 0.
            result --; 

            Debug.Assert(result >= 0x00 && result <=0x0A, "ContentAlignmentToIndex result out of range."); 
            Debug.Assert(result != 0x00 || alignment == ContentAlignment.TopLeft, "Error detected in ContentAlignmentToIndex."); 
            Debug.Assert(result != 0x01 || alignment == ContentAlignment.TopCenter, "Error detected in ContentAlignmentToIndex.");
            Debug.Assert(result != 0x02 || alignment == ContentAlignment.TopRight, "Error detected in ContentAlignmentToIndex."); 
            Debug.Assert(result != 0x03, "Error detected in ContentAlignmentToIndex.");
            Debug.Assert(result != 0x04 || alignment == ContentAlignment.MiddleLeft, "Error detected in ContentAlignmentToIndex.");
            Debug.Assert(result != 0x05 || alignment == ContentAlignment.MiddleCenter, "Error detected in ContentAlignmentToIndex.");
            Debug.Assert(result != 0x06 || alignment == ContentAlignment.MiddleRight, "Error detected in ContentAlignmentToIndex."); 
            Debug.Assert(result != 0x07, "Error detected in ContentAlignmentToIndex.");
            Debug.Assert(result != 0x08 || alignment == ContentAlignment.BottomLeft, "Error detected in ContentAlignmentToIndex."); 
            Debug.Assert(result != 0x09 || alignment == ContentAlignment.BottomCenter, "Error detected in ContentAlignmentToIndex."); 
            Debug.Assert(result != 0x0A || alignment == ContentAlignment.BottomRight, "Error detected in ContentAlignmentToIndex.");
 
            return result;
        }

        // Converts 0x00, 0x01, 0x02, 0x04 (3b flag) to 0, 1, 2, 3 (2b index) 
        private static byte xContentAlignmentToIndex(int threeBitFlag) {
            Debug.Assert(threeBitFlag >= 0x00 && threeBitFlag <= 0x04 && threeBitFlag != 0x03, "threeBitFlag out of range."); 
            byte result = threeBitFlag == 0x04 ? (byte) 3 : (byte) threeBitFlag; 
            Debug.Assert((result & 0x03) == result, "Result out of range.");
            return result; 
        }

        public static Size ConvertZeroToUnbounded(Size size) {
            if(size.Width == 0) size.Width = Int32.MaxValue; 
            if(size.Height == 0) size.Height = Int32.MaxValue;
            return size; 
        } 

        // Clamps negative values in Padding struct to zero. 
        public static Padding ClampNegativePaddingToZero(Padding padding) {
            // Careful: Setting the LRTB properties causes Padding.All to be -1 even if LRTB all agree.
            if(padding.All < 0) {
                padding.Left = Math.Max(0, padding.Left); 
                padding.Top = Math.Max(0, padding.Top);
                padding.Right = Math.Max(0, padding.Right); 
                padding.Bottom = Math.Max(0, padding.Bottom); 
            }
            return padding; 
        }

        /*
         *  Maps an anchor to its opposite.  Does not support combinations.  None returns none. 
         *
         *  Top     = 0x01 
         *  Bottom  = 0x02 
         *  Left    = 0x04
         *  Right   = 0x08 
         */


        // Returns the positive opposite of the given anchor (e.g., L -> R, LT -> RB, LTR -> LBR, etc.).  None return none. 
        private static AnchorStyles GetOppositeAnchor(AnchorStyles anchor) {
            AnchorStyles result = AnchorStyles.None; 
            if (anchor == AnchorStyles.None){ 
                return result;
            } 

            // iterate through T,B,L,R
            // bitwise or      B,T,R,L as appropriate
            for (int i = 1; i <= (int)AnchorStyles.Right; i=i <<1) { 
                switch (anchor & (AnchorStyles)i) {
                   case AnchorStyles.None: 
                        break; 
                   case AnchorStyles.Left:
                       result |= AnchorStyles.Right; 
                       break;
                   case AnchorStyles.Top:
                       result |= AnchorStyles.Bottom;
                       break; 
                   case AnchorStyles.Right:
                        result |= AnchorStyles.Left; 
                        break; 
                   case AnchorStyles.Bottom:
                       result |= AnchorStyles.Top; 
                       break;
                   default:
                        break;
               } 

            } 
 
            return result;
        } 

        public static TextImageRelation GetOppositeTextImageRelation(TextImageRelation relation) {
            return (TextImageRelation) GetOppositeAnchor((AnchorStyles)relation);
        } 

        public static Size UnionSizes(Size a, Size b) { 
            return new Size( 
                Math.Max(a.Width, b.Width),
                Math.Max(a.Height, b.Height)); 
        }

        public static Size IntersectSizes(Size a, Size b) {
            return new Size( 
                Math.Min(a.Width, b.Width),
                Math.Min(a.Height, b.Height)); 
        } 

        public static bool IsIntersectHorizontally(Rectangle rect1, Rectangle rect2) { 
            if (!rect1.IntersectsWith(rect2)) {
                return false;
            }
            if (rect1.X <= rect2.X && rect1.X + rect1.Width >= rect2.X + rect2.Width) { 
                //rect 1 contains rect 2 horizontally
                return true; 
                } 
            if (rect2.X <= rect1.X && rect2.X + rect2.Width >= rect1.X + rect1.Width) {
                //rect 2 contains rect 1 horizontally 
                return true;
            }
            return false;
        } 

        public static bool IsIntersectVertically(Rectangle rect1, Rectangle rect2) { 
            if (!rect1.IntersectsWith(rect2)) { 
                return false;
            } 
            if (rect1.Y <= rect2.Y && rect1.Y + rect1.Width >= rect2.Y + rect2.Width) {
                //rect 1 contains rect 2 vertically
                return true;
                } 
            if (rect2.Y <= rect1.Y && rect2.Y + rect2.Width >= rect1.Y + rect1.Width) {
                //rect 2 contains rect 1 vertically 
                return true; 
            }
            return false; 
        }

        //returns anchorStyles, transforms from DockStyle if necessary
        internal static AnchorStyles GetUnifiedAnchor(IArrangedElement element) { 
            DockStyle dockStyle = DefaultLayout.GetDock(element);
            if (dockStyle != DockStyle.None) { 
                return dockingToAnchor[(int)dockStyle]; 
            }
            return DefaultLayout.GetAnchor(element); 
        }

        public static Rectangle AlignAndStretch(Size fitThis, Rectangle withinThis, AnchorStyles anchorStyles) {
            return Align(Stretch(fitThis, withinThis.Size, anchorStyles), withinThis, anchorStyles); 
        }
 
        public static Rectangle Align(Size alignThis, Rectangle withinThis, AnchorStyles anchorStyles) { 
            return VAlign(alignThis, HAlign(alignThis, withinThis, anchorStyles), anchorStyles);
        } 

        public static Rectangle Align(Size alignThis, Rectangle withinThis, ContentAlignment align) {
            return VAlign(alignThis, HAlign(alignThis, withinThis, align), align);
        } 

        public static Rectangle HAlign(Size alignThis, Rectangle withinThis, AnchorStyles anchorStyles) { 
            if ((anchorStyles & AnchorStyles.Right) != 0) { 
                withinThis.X += withinThis.Width - alignThis.Width;
            } 
            else if (anchorStyles == AnchorStyles.None || (anchorStyles & HorizontalAnchorStyles) == 0) {
                withinThis.X += (withinThis.Width - alignThis.Width) / 2;
            }
            withinThis.Width = alignThis.Width; 

            return withinThis; 
        } 

        private static Rectangle HAlign(Size alignThis, Rectangle withinThis, ContentAlignment align) { 
            if ((align & AnyRight) != 0) {
                withinThis.X += withinThis.Width - alignThis.Width;
            }
            else if ((align & AnyCenter) != 0) { 
                withinThis.X += (withinThis.Width - alignThis.Width) / 2;
            } 
            withinThis.Width = alignThis.Width; 

            return withinThis; 
        }

        public static Rectangle VAlign(Size alignThis, Rectangle withinThis, AnchorStyles anchorStyles) {
            if ((anchorStyles & AnchorStyles.Bottom) != 0) { 
                withinThis.Y += withinThis.Height - alignThis.Height;
            } 
            else if (anchorStyles == AnchorStyles.None || (anchorStyles & VerticalAnchorStyles) == 0) { 
                withinThis.Y += (withinThis.Height - alignThis.Height) / 2;
            } 

            withinThis.Height = alignThis.Height;

            return withinThis; 
        }
 
        public static Rectangle VAlign(Size alignThis, Rectangle withinThis, ContentAlignment align) { 
            if ((align & AnyBottom) != 0) {
                withinThis.Y += withinThis.Height - alignThis.Height; 
            }
            else if ((align & AnyMiddle) != 0) {
                withinThis.Y += (withinThis.Height - alignThis.Height) / 2;
            } 

            withinThis.Height = alignThis.Height; 
 
            return withinThis;
        } 

        public static Size Stretch(Size stretchThis, Size withinThis, AnchorStyles anchorStyles) {
            Size stretchedSize =  new Size(
                (anchorStyles & HorizontalAnchorStyles) == HorizontalAnchorStyles ? withinThis.Width : stretchThis.Width, 
                (anchorStyles & VerticalAnchorStyles) == VerticalAnchorStyles ? withinThis.Height : stretchThis.Height
            ); 
            if (stretchedSize.Width > withinThis.Width) { 
                stretchedSize.Width = withinThis.Width;
            } 
            if (stretchedSize.Height > withinThis.Height) {
                stretchedSize.Height = withinThis.Height;
            }
            return stretchedSize; 
        }
 
        public static Rectangle InflateRect(Rectangle rect, Padding padding) { 
            rect.X -= padding.Left;
            rect.Y -= padding.Top; 
            rect.Width += padding.Horizontal;
            rect.Height += padding.Vertical;
            return rect;
        } 

        public static Rectangle DeflateRect(Rectangle rect, Padding padding) { 
            rect.X += padding.Left; 
            rect.Y += padding.Top;
            rect.Width -= padding.Horizontal; 
            rect.Height -= padding.Vertical;
            return rect;
        }
 
        public static Size AddAlignedRegion(Size textSize, Size imageSize, TextImageRelation relation) {
            return AddAlignedRegionCore(textSize, imageSize, IsVerticalRelation(relation)); 
        } 

        public static Size AddAlignedRegionCore(Size currentSize, Size contentSize, bool vertical) { 
            if(vertical) {
                currentSize.Width = Math.Max(currentSize.Width, contentSize.Width);
                currentSize.Height += contentSize.Height;
            } else { 
                currentSize.Width += contentSize.Width;
                currentSize.Height = Math.Max(currentSize.Height, contentSize.Height); 
            } 
            return currentSize;
        } 

        public static Padding FlipPadding(Padding padding) {
            // If Padding.All != -1, then TLRB are all the same and there is no work to be done.
            if(padding.All != -1) { 
                return padding;
            } 
 
            // Padding is a stuct (passed by value, no need to make a copy)
            int temp; 

            temp = padding.Top;
            padding.Top = padding.Left;
            padding.Left = temp; 

            temp = padding.Bottom; 
            padding.Bottom = padding.Right; 
            padding.Right = temp;
 
            return padding;
        }

        public static Point FlipPoint(Point point) { 
            // Point is a struct (passed by value, no need to make a copy)
            int temp = point.X; 
            point.X = point.Y; 
            point.Y = temp;
            return point; 
        }

        public static Rectangle FlipRectangle(Rectangle rect) {
            // Rectangle is a stuct (passed by value, no need to make a copy) 
            rect.Location = FlipPoint(rect.Location);
            rect.Size = FlipSize(rect.Size); 
            return rect; 
        }
 
        public static Rectangle FlipRectangleIf(bool condition, Rectangle rect) {
            return condition ? FlipRectangle(rect) : rect;
        }
 
        public static Size FlipSize(Size size) {
            // Size is a struct (passed by value, no need to make a copy) 
            int temp = size.Width; 
            size.Width = size.Height;
            size.Height = temp; 
            return size;
        }

        public static Size FlipSizeIf(bool condition, Size size) { 
            return condition ? FlipSize(size) : size;
        } 
 
        public static bool IsHorizontalAlignment(ContentAlignment align) {
            return !IsVerticalAlignment(align); 
        }

        // True if text & image should be lined up horizontally.  False if vertical or overlay.
        public static bool IsHorizontalRelation(TextImageRelation relation) { 
            return (relation & (TextImageRelation.TextBeforeImage | TextImageRelation.ImageBeforeText)) != 0;
        } 
 
        public static bool IsVerticalAlignment(ContentAlignment align) {
            Debug.Assert(align != ContentAlignment.MiddleCenter, "Result is ambiguous with an alignment of MiddleCenter."); 
            return (align & (ContentAlignment.TopCenter | ContentAlignment.BottomCenter)) != 0;
        }

        // True if text & image should be lined up vertically.  False if horizontal or overlay. 
        public static bool IsVerticalRelation(TextImageRelation relation) {
            return (relation & (TextImageRelation.TextAboveImage | TextImageRelation.ImageAboveText)) != 0; 
        } 

        public static bool IsZeroWidthOrHeight(Rectangle rectangle) { 
            return (rectangle.Width == 0 || rectangle.Height == 0);
        }

        public static bool IsZeroWidthOrHeight(Size size) { 
            return (size.Width == 0 || size.Height == 0);
        } 
 
        public static bool AreWidthAndHeightLarger(Size size1, Size size2){
            return ((size1.Width >= size2.Width) && (size1.Height >= size2.Height)); 
        }

        public static void SplitRegion(Rectangle bounds, Size specifiedContent, AnchorStyles region1Align, out Rectangle region1, out Rectangle region2) {
            region1 = region2 = bounds; 
            switch(region1Align) {
                case AnchorStyles.Left: 
                    region1.Width = specifiedContent.Width; 
                    region2.X += specifiedContent.Width;
                    region2.Width -= specifiedContent.Width; 
                    break;
                case AnchorStyles.Right:
                    region1.X += bounds.Width - specifiedContent.Width;
                    region1.Width = specifiedContent.Width; 
                    region2.Width -= specifiedContent.Width;
                    break; 
                case AnchorStyles.Top: 
                    region1.Height = specifiedContent.Height;
                    region2.Y += specifiedContent.Height; 
                    region2.Height -= specifiedContent.Height;
                    break;
                case AnchorStyles.Bottom:
                    region1.Y += bounds.Height - specifiedContent.Height; 
                    region1.Height = specifiedContent.Height;
                    region2.Height -= specifiedContent.Height; 
                    break; 
                default:
                    Debug.Fail("Unsupported value for region1Align."); 
                    break;
            }

            Debug.Assert(Rectangle.Union(region1, region2) == bounds, 
                "Regions do not add up to bounds.");
        } 
 
        // Expands adjacent regions to bounds.  region1Align indicates which way the adjacency occurs.
        public static void ExpandRegionsToFillBounds(Rectangle bounds, AnchorStyles region1Align, ref Rectangle region1, ref Rectangle region2) { 
            switch(region1Align) {
                case AnchorStyles.Left:
                    Debug.Assert(region1.Right == region2.Left, "Adjacency error.");
                    region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Right); 
                    region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Left);
                    break; 
                case AnchorStyles.Right: 
                    Debug.Assert(region2.Right == region1.Left, "Adjacency error.");
                    region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Left); 
                    region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Right);
                    break;
                case AnchorStyles.Top:
                    Debug.Assert(region1.Bottom == region2.Top, "Adjacency error."); 
                    region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Bottom);
                    region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Top); 
                    break; 
                case AnchorStyles.Bottom:
                    Debug.Assert(region2.Bottom == region1.Top, "Adjacency error."); 
                    region1 = SubstituteSpecifiedBounds(bounds, region1, AnchorStyles.Top);
                    region2 = SubstituteSpecifiedBounds(bounds, region2, AnchorStyles.Bottom);
                    break;
                default: 
                    Debug.Fail("Unsupported value for region1Align.");
                    break; 
            } 
            Debug.Assert(Rectangle.Union(region1, region2) == bounds, "region1 and region2 do not add up to bounds.");
        } 

        public static Size SubAlignedRegion(Size currentSize, Size contentSize, TextImageRelation relation) {
            return SubAlignedRegionCore(currentSize, contentSize, IsVerticalRelation(relation));
        } 

        public static Size SubAlignedRegionCore(Size currentSize, Size contentSize, bool vertical) { 
            if(vertical) { 
                currentSize.Height -= contentSize.Height;
            } else { 
                currentSize.Width -= contentSize.Width;
            }
            return currentSize;
        } 

        private static Rectangle SubstituteSpecifiedBounds(Rectangle originalBounds, Rectangle substitutionBounds, AnchorStyles specified) { 
            int left = (specified & AnchorStyles.Left) != 0 ? substitutionBounds.Left : originalBounds.Left; 
            int top = (specified & AnchorStyles.Top) != 0 ? substitutionBounds.Top : originalBounds.Top;
            int right = (specified & AnchorStyles.Right) != 0 ? substitutionBounds.Right : originalBounds.Right; 
            int bottom = (specified & AnchorStyles.Bottom) != 0 ? substitutionBounds.Bottom : originalBounds.Bottom;
            return Rectangle.FromLTRB(left, top, right, bottom);
        }
 
        // given a rectangle, flip to the other side of (withinBounds)
        // 
        // Never call this if you derive from ScrollableControl 
        public static Rectangle RTLTranslate(Rectangle bounds, Rectangle withinBounds) {
            bounds.X = withinBounds.Width - bounds.Right; 
            return bounds;
        }

        /// MeasureTextCache 
        /// Cache mechanism added for VSWhidbey 500516
        /// 3000 character strings take 9 seconds to load the form 
        public sealed class MeasureTextCache { 
              private Size unconstrainedPreferredSize = LayoutUtils.InvalidSize;
              private const int MaxCacheSize = 6;           // the number of preferred sizes to store 
              private int nextCacheEntry = -1;              // the next place in the ring buffer to store a preferred size

              private PreferredSizeCache[] sizeCacheList;   // MRU of size MaxCacheSize
 

              /// InvalidateCache 
              /// Clears out the cached values, should be called whenever Text, Font or a TextFormatFlag has changed 
              public void InvalidateCache() {
                  unconstrainedPreferredSize = LayoutUtils.InvalidSize; 
                  sizeCacheList = null;
              }

 
              /// GetTextSize
              /// Given constraints, format flags a font and text, determine the size of the string 
              /// employs an MRU of the last several constraints passed in via a ring-buffer of size MaxCacheSize. 
              /// Assumes Text and TextFormatFlags are the same, if either were to change, a call to
              /// InvalidateCache should be made 
              public Size GetTextSize(string text, Font font, Size proposedConstraints, TextFormatFlags flags) {


                  if (!TextRequiresWordBreak(text, font, proposedConstraints, flags)) { 
                      // Text fits within proposed width
 
                      // IF we're here, this means we've got text that can fit into the proposedConstraints 
                      // without wrapping.  We've determined this because our
 
                      // as a side effect of calling TextRequiresWordBreak,
                      // unconstrainedPreferredSize is set.
                      return unconstrainedPreferredSize;
                  } 
                  else {
                      // Text does NOT fit within proposed width - requires WordBreak 
 
                      // IF we're here, this means that the wrapping width is smaller
                      // than our max width.  For example: we measure the text with infinite 
                      // bounding box and we determine the width to fit all the characters
                      // to be 200 px wide.  We would come here only for proposed widths less
                      // than 200 px.
 

                      // Create our ring buffer if we dont have one 
                      if (sizeCacheList == null) { 
                          sizeCacheList = new PreferredSizeCache[MaxCacheSize];
                      } 

                      // check the existing constraints from previous calls
                      foreach (PreferredSizeCache sizeCache in sizeCacheList) {
 
                          if (sizeCache.ConstrainingSize == proposedConstraints) {
                              return sizeCache.PreferredSize; 
                          } 
                          else if ((sizeCache.ConstrainingSize.Width == proposedConstraints.Width)
                                    && (sizeCache.PreferredSize.Height <= proposedConstraints.Height)) { 

                              // Caching a common case where the width matches perfectly, and the stored preferred height
                              // is smaller or equal to the constraining size.
                              //        prefSize = GetPreferredSize(w,Int32.MaxValue); 
                              //        prefSize = GetPreferredSize(w,prefSize.Height);
 
                              return sizeCache.PreferredSize; 
                          }
                          // 
                      }

                      // if we've gotten here, it means we dont have a cache entry, therefore
                      // we should add a new one in the next available slot. 
                      Size prefSize = TextRenderer.MeasureText(text, font, proposedConstraints, flags);
                      nextCacheEntry = (nextCacheEntry+1)%MaxCacheSize; 
                      sizeCacheList[nextCacheEntry] = new PreferredSizeCache(proposedConstraints, prefSize); 

                      return prefSize; 

                  }

              } 

              /// GetUnconstrainedSize 
              /// Gets the unconstrained (Int32.MaxValue, Int32.MaxValue) size for a piece of text 
              private Size GetUnconstrainedSize(string text, Font font, TextFormatFlags flags) {
 
                  if (unconstrainedPreferredSize == LayoutUtils.InvalidSize) {
                      // we also investigated setting the SingleLine flag, however this did not yield as much benefit as the word break
                      // and had possibility of causing internationalization issues.
 
                      flags = (flags & ~TextFormatFlags.WordBreak); // rip out the wordbreak flag
                      unconstrainedPreferredSize = TextRenderer.MeasureText(text, font, LayoutUtils.MaxSize, flags); 
                  } 
                  return unconstrainedPreferredSize;
              } 


              /// TextRequiresWordBreak
              /// If you give the text all the space in the world it wants, then there should be no reason 
              /// for it to break on a word.  So we find out what the unconstrained size is (Int32.MaxValue, Int32.MaxValue)
              /// for a string - eg. 35, 13.  If the size passed in has a larger width than 35, then we know that 
              /// the WordBreak flag is not necessary. 
              public bool TextRequiresWordBreak(string text, Font font, Size size, TextFormatFlags flags) {
 
                  // if the unconstrained size of the string is larger than the proposed width
                  // we need the word break flag, otherwise we dont, its a perf hit to use it.
                  return GetUnconstrainedSize(text, font, flags).Width > size.Width;
              } 

              private struct PreferredSizeCache { 
                  public PreferredSizeCache(Size constrainingSize, Size preferredSize) { 
                     this.ConstrainingSize = constrainingSize;
                     this.PreferredSize = preferredSize; 
                  }
                  public Size ConstrainingSize;
                  public Size PreferredSize;
               } 

          } 
 

    } 

    // Frequently when you need to do a PreformLayout, you also need to invalidate the
    // PreferredSizeCache (you are laying out because you know that the action has changed
    // the PreferredSize of the control and/or its container).  LayoutTransaction wraps both 
    // of these operations into one, plus adds a check for null to make our code more
    // concise. 
    // 
    // Usage1: (When we are not calling to other code which may cause a layout:)
    // 
    //  LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds);
    //
    // Usage2: (When we need to wrap code which may cause additional layouts:)
    // 
    //  using(new LayoutTransaction(ParentInternal, this, PropertyNames.Bounds)) {
    //      OnBoundsChanged(); 
    //  } 
    //
    // The second usage spins off 12b for garbage collection, but I did some profiling and 
    // it didn't seem significant (we were spinning off more from LayoutEventArgs.)
    internal sealed class LayoutTransaction : IDisposable {
        Control _controlToLayout;
        bool _resumeLayout; 

#if DEBUG 
        int _layoutSuspendCount; 
#endif
        public LayoutTransaction(Control controlToLayout, IArrangedElement controlCausingLayout, string property) : 
            this(controlToLayout, controlCausingLayout, property, true) {
        }

        public LayoutTransaction(Control controlToLayout, IArrangedElement controlCausingLayout, string property, bool resumeLayout) { 
            CommonProperties.xClearPreferredSizeCache(controlCausingLayout);
            _controlToLayout = controlToLayout; 
 
            _resumeLayout = resumeLayout;
            if(_controlToLayout != null) { 
#if DEBUG
                _layoutSuspendCount = _controlToLayout.LayoutSuspendCount;
#endif
                _controlToLayout.SuspendLayout(); 
                CommonProperties.xClearPreferredSizeCache(_controlToLayout);
 
                // Same effect as calling performLayout on Dispose but then we would have to keep 
                // controlCausingLayout and property around as state.
                if (resumeLayout) { 
                    _controlToLayout.PerformLayout(new LayoutEventArgs(controlCausingLayout, property));
                }
            }
        } 

        public void Dispose() { 
            if(_controlToLayout != null) { 
                _controlToLayout.ResumeLayout(_resumeLayout);
 
#if DEBUG
                Debug.Assert(_controlToLayout.LayoutSuspendCount == _layoutSuspendCount, "Suspend/Resume layout mismatch!");
#endif
            } 
        }
 
        // This overload should be used when a property has changed that affects preferred size, 
        // but you only want to layout if a certain condition exists (say you want to layout your
        // parent because your preferred size has changed). 
        public static IDisposable CreateTransactionIf(bool condition, Control controlToLayout,  IArrangedElement elementCausingLayout, string property)  {
            if (condition) {
                return new LayoutTransaction(controlToLayout,  elementCausingLayout, property);
            } 
            else {
                CommonProperties.xClearPreferredSizeCache(elementCausingLayout); 
                return new NullLayoutTransaction(); 

            } 
        }


        public static void DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, string property) { 
            if (elementCausingLayout != null)  {
                CommonProperties.xClearPreferredSizeCache(elementCausingLayout); 
                if(elementToLayout != null) { 
                    CommonProperties.xClearPreferredSizeCache(elementToLayout);
                    elementToLayout.PerformLayout(elementCausingLayout, property); 

                }
            }
            Debug.Assert(elementCausingLayout != null, "LayoutTransaction.DoLayout - elementCausingLayout is null, no layout performed - did you mix up your parameters?"); 

        } 
 

        // This overload should be used when a property has changed that affects preferred size, 
        // but you only want to layout if a certain condition exists (say you want to layout your
        // parent because your preferred size has changed).
        public static void DoLayoutIf(bool condition, IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, string property) {
            if (!condition) { 
                 if (elementCausingLayout != null)  {
                     CommonProperties.xClearPreferredSizeCache(elementCausingLayout); 
                 } 
            }
            else { 
                LayoutTransaction.DoLayout(elementToLayout, elementCausingLayout, property);
            }
        }
 
    }
    internal struct NullLayoutTransaction : IDisposable { 
         public void Dispose() { 
         }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK