|
//------------------------------------------------------------------------------
// <copyright file="WinFormsUtils.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms.Layout {
using System;
using System.Collections.Specialized;
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
// Some LayoutEngines extend the same properties to their children. We want
// these extended properties to retain their value when moved from one container
// to another. (For example, set BoxStretchInternal on a control in FlowPanel and then move
// the control to GridPanel.) CommonProperties is a place to define keys and
// accessors for such properties.
internal class CommonProperties {
private static readonly int _layoutStateProperty = PropertyStore.CreateKey();
private static readonly int _specifiedBoundsProperty = PropertyStore.CreateKey();
private static readonly int _preferredSizeCacheProperty = PropertyStore.CreateKey();
private static readonly int _paddingProperty = PropertyStore.CreateKey();
private static readonly int _marginProperty = PropertyStore.CreateKey();
private static readonly int _minimumSizeProperty = PropertyStore.CreateKey();
private static readonly int _maximumSizeProperty = PropertyStore.CreateKey();
private static readonly int _layoutBoundsProperty = PropertyStore.CreateKey();
#if DEBUG
private static readonly int _lastKnownStateProperty = PropertyStore.CreateKey();
#endif
internal const ContentAlignment DefaultAlignment = ContentAlignment.TopLeft;
internal const AnchorStyles DefaultAnchor = AnchorStyles.Top | AnchorStyles.Left;
internal const bool DefaultAutoSize = false;
internal const DockStyle DefaultDock = DockStyle.None;
internal static readonly Padding DefaultMargin = new Padding(3);
internal static readonly Size DefaultMinimumSize = new Size(0, 0);
internal static readonly Size DefaultMaximumSize = new Size(0, 0);
// DO NOT MOVE THE FOLLOWING 4 SECTIONS
// We have done some special arranging here so that if the first 7 bits of state are zero, we know
// that the control is purely absolutely positioned and DefaultLayout does not need to do
// anything.
//
private static readonly BitVector32.Section _dockAndAnchorNeedsLayoutSection = BitVector32.CreateSection(0x7F);
private static readonly BitVector32.Section _dockAndAnchorSection = BitVector32.CreateSection(0x0F);
private static readonly BitVector32.Section _dockModeSection = BitVector32.CreateSection(0x01, _dockAndAnchorSection);
private static readonly BitVector32.Section _autoSizeSection = BitVector32.CreateSection(0x01, _dockModeSection);
private static readonly BitVector32.Section _BoxStretchInternalSection = BitVector32.CreateSection(0x03, _autoSizeSection);
private static readonly BitVector32.Section _anchorNeverShrinksSection = BitVector32.CreateSection(0x01, _BoxStretchInternalSection);
private static readonly BitVector32.Section _flowBreakSection = BitVector32.CreateSection(0x01, _anchorNeverShrinksSection);
private static readonly BitVector32.Section _selfAutoSizingSection = BitVector32.CreateSection(0x01, _flowBreakSection);
private static readonly BitVector32.Section _autoSizeModeSection = BitVector32.CreateSection(0x01, _selfAutoSizingSection);
private enum DockAnchorMode{
Anchor = 0,
Dock = 1
}
#region AppliesToAllLayouts
/// ClearMaximumSize
/// Removes the maximum size from the property store, making it "unset".
///
internal static void ClearMaximumSize(IArrangedElement element) {
if (element.Properties.ContainsObject(_maximumSizeProperty)) {
element.Properties.RemoveObject(_maximumSizeProperty);
}
}
/// GetAutoSize
/// Determines whether or not the System.Windows.Forms.Layout LayoutEngines
/// think the element is AutoSized.
///
/// A control can thwart the layout engine by overriding its virtual AutoSize
/// property and not calling base. If CommonProperties.GetAutoSize(element) is false,
/// a layout engine will treat it as AutoSize = false and not size the element to its
/// preferred size.
internal static bool GetAutoSize(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
int value = state[_autoSizeSection];
return value != 0;
}
/// GetMargin
/// Returns the Margin (exterior space) for an item
///
/// We can not use our pattern of passing the default value into Margin because the
/// LayoutEngines read this property and do not know each element's DefaultMargin.
/// Instead the Element sets the Margin in its ctor.
internal static Padding GetMargin(IArrangedElement element) {
bool found;
Padding padding = element.Properties.GetPadding(_marginProperty, out found);
if (found) {
return padding;
}
return DefaultMargin;
}
/// GetMaximumSize
/// Returns the maximum size for an element
internal static Size GetMaximumSize(IArrangedElement element, Size defaultMaximumSize) {
bool found;
Size size = element.Properties.GetSize(_maximumSizeProperty, out found);
if (found) {
return size;
}
return defaultMaximumSize;
}
/// GetMinimumSize
/// Returns the minimum size for an element
internal static Size GetMinimumSize(IArrangedElement element, Size defaultMinimumSize) {
bool found;
Size size = element.Properties.GetSize(_minimumSizeProperty, out found);
if (found) {
return size;
}
return defaultMinimumSize;
}
/// GetPadding
/// Returns the padding for an element
/// Typically the padding is accounted for in either the DisplayRectangle calculation
/// and/or the GetPreferredSize calculation of a control.
///
/// NOTE: LayoutEngines should never read this property. Padding gets incorperated into
/// layout by modifying what the control reports for preferred size.
internal static Padding GetPadding(IArrangedElement element, Padding defaultPadding) {
bool found;
Padding padding = element.Properties.GetPadding(_paddingProperty, out found);
if (found) {
return padding;
}
return defaultPadding;
}
/// GetSpecifiedBounds
/// Returns the last size manually set into the element. See UpdateSpecifiedBounds.
internal static Rectangle GetSpecifiedBounds(IArrangedElement element) {
bool found;
Rectangle rectangle = element.Properties.GetRectangle(_specifiedBoundsProperty, out found);
if (found && rectangle != LayoutUtils.MaxRectangle) {
return rectangle;
}
return element.Bounds;
}
/// ResetPadding
/// clears out the padding from the property store
internal static void ResetPadding(IArrangedElement element) {
object value = element.Properties.GetObject(_paddingProperty);
if(value != null) {
element.Properties.RemoveObject(_paddingProperty);
}
}
/// SetAutoSize
/// Sets whether or not the layout engines should treat this control as auto sized.
internal static void SetAutoSize(IArrangedElement element, bool value) {
Debug.Assert(value != GetAutoSize(element), "PERF: Caller should guard against setting AutoSize to original value.");
BitVector32 state = GetLayoutState(element);
state[_autoSizeSection] = value ? 1 : 0;
SetLayoutState(element, state);
if(value == false) {
// If autoSize is being turned off, restore the control to its specified bounds.
element.SetBounds(GetSpecifiedBounds(element), BoundsSpecified.None);
}
Debug.Assert(GetAutoSize(element) == value, "Error detected setting AutoSize.");
}
/// SetMargin
/// Sets the margin (exterior space) for an element.
internal static void SetMargin(IArrangedElement element, Padding value) {
Debug.Assert(value != GetMargin(element), "PERF: Caller should guard against setting Margin to original value.");
element.Properties.SetPadding(_marginProperty, value);
Debug.Assert(GetMargin(element) == value, "Error detected setting Margin.");
LayoutTransaction.DoLayout(element.Container, element, PropertyNames.Margin);
}
/// SetMaximumSize
/// Sets the maximum size for an element.
internal static void SetMaximumSize(IArrangedElement element, Size value) {
Debug.Assert(value != GetMaximumSize(element, new Size(-7109, -7107)),
"PERF: Caller should guard against setting MaximumSize to original value.");
element.Properties.SetSize(_maximumSizeProperty, value);
// Element bounds may need to truncated to new maximum
//
Rectangle bounds = element.Bounds;
bounds.Width = Math.Min(bounds.Width, value.Width);
bounds.Height = Math.Min(bounds.Height, value.Height);
element.SetBounds(bounds, BoundsSpecified.Size);
// element.SetBounds does a SetBoundsCore. We still need to explicitly refresh parent layout.
LayoutTransaction.DoLayout(element.Container, element, PropertyNames.MaximumSize);
Debug.Assert(GetMaximumSize(element, new Size(-7109, -7107)) == value, "Error detected setting MaximumSize.");
}
/// SetMinimumSize
/// Sets the minimum size for an element.
internal static void SetMinimumSize(IArrangedElement element, Size value) {
Debug.Assert(value != GetMinimumSize(element, new Size(-7109, -7107)),
"PERF: Caller should guard against setting MinimumSize to original value.");
element.Properties.SetSize(_minimumSizeProperty, value);
using (new LayoutTransaction(element.Container as Control, element, PropertyNames.MinimumSize)) {
// Element bounds may need to inflated to new minimum
//
Rectangle bounds = element.Bounds;
bounds.Width = Math.Max(bounds.Width, value.Width);
bounds.Height = Math.Max(bounds.Height, value.Height);
element.SetBounds(bounds, BoundsSpecified.Size);
}
Debug.Assert(GetMinimumSize(element, new Size(-7109, -7107)) == value, "Error detected setting MinimumSize.");
}
/// SetPadding
/// Sets the padding (interior space) for an element. See GetPadding for more detiails.
/// NOTE: It is the callers responsibility to do layout. See Control.Padding for details.
internal static void SetPadding(IArrangedElement element, Padding value) {
Debug.Assert(value != GetPadding(element, new Padding(-7105)),
"PERF: Caller should guard against setting Padding to original value.");
value = LayoutUtils.ClampNegativePaddingToZero(value);
element.Properties.SetPadding(_paddingProperty, value);
Debug.Assert(GetPadding(element, new Padding(-7105)) == value, "Error detected setting Padding.");
}
/// UpdateSpecifiedBounds
/// The main purpose of this function is to remember what size someone specified in the Size, Width, Height, Bounds
/// property. (Its the whole reason the BoundsSpecified enum exists.) Consider this scenario. You set a Button
/// to DockStyle.Fill, then DockStyle.None. When Dock.Filled, the Size changed to 300,300. When you
/// set it back to DockStyle.None, the size switches back to 100,23. How does this happen?
///
/// Setting the control to Dock.Fill (via DefaultLayout engine)
/// element.SetBounds(newElementBounds, BoundsSpecified.None);
///
/// (If someone happens to set the Size property here the specified bounds gets updated via Control.Size)
/// SetBounds(x, y, value.Width, value.Height, BoundsSpecified.Size);
///
/// Setting the control to Dock.None (via DefaultLayout.SetDock)
/// element.SetBounds(CommonProperties.GetSpecifiedBounds(element), BoundsSpecified.None);
internal static void UpdateSpecifiedBounds(IArrangedElement element, int x, int y, int width, int height, BoundsSpecified specified) {
Rectangle originalBounds = CommonProperties.GetSpecifiedBounds(element);
// PERF note: Bitwise operator usage intentional to optimize out branching.
bool xChangedButNotSpecified = ((specified & BoundsSpecified.X) == BoundsSpecified.None) & x != originalBounds.X;
bool yChangedButNotSpecified = ((specified & BoundsSpecified.Y) == BoundsSpecified.None) & y != originalBounds.Y;
bool wChangedButNotSpecified = ((specified & BoundsSpecified.Width) == BoundsSpecified.None) & width != originalBounds.Width;
bool hChangedButNotSpecified = ((specified & BoundsSpecified.Height) == BoundsSpecified.None) & height != originalBounds.Height;
if(xChangedButNotSpecified | yChangedButNotSpecified | wChangedButNotSpecified | hChangedButNotSpecified) {
// if any of them are changed and specified cache the new value.
if (!xChangedButNotSpecified) originalBounds.X = x;
if (!yChangedButNotSpecified) originalBounds.Y = y;
if (!wChangedButNotSpecified) originalBounds.Width = width;
if (!hChangedButNotSpecified) originalBounds.Height = height;
element.Properties.SetRectangle(_specifiedBoundsProperty, originalBounds);
} else {
// SetBoundsCore is going to call this a lot with the same bounds. Avoid the set object
// (which indirectly may causes an allocation) if we can.
if(element.Properties.ContainsObject(_specifiedBoundsProperty)) {
// use MaxRectangle instead of null so we can reuse the SizeWrapper in the property store.
element.Properties.SetRectangle(_specifiedBoundsProperty, LayoutUtils.MaxRectangle);
}
}
}
// Used by ToolStripControlHost.Size.
internal static void UpdateSpecifiedBounds(IArrangedElement element, int x, int y, int width, int height) {
Rectangle bounds = new Rectangle(x, y, width, height);
element.Properties.SetRectangle(_specifiedBoundsProperty, bounds);
}
/// xClearPreferredSizeCache
/// clears the preferred size cached for any control that overrides
/// the internal GetPreferredSizeCore method. DO NOT CALL DIRECTLY
/// unless it is understood how the size of the control is going to be updated.
///
internal static void xClearPreferredSizeCache(IArrangedElement element) {
element.Properties.SetSize(_preferredSizeCacheProperty, LayoutUtils.InvalidSize);
#if DEBUG
Debug_ClearProperties(element);
#endif
Debug.Assert(xGetPreferredSizeCache(element) == Size.Empty, "Error detected in xClearPreferredSizeCache.");
}
/// xClearAllPreferredSizeCaches
/// clears all the caching for an IArrangedElement hierarchy
/// typically done in dispose.
internal static void xClearAllPreferredSizeCaches(IArrangedElement start) {
CommonProperties.xClearPreferredSizeCache(start);
ArrangedElementCollection controlsCollection = start.Children;
// This may have changed the sizes of our children.
// PERFNOTE: This is more efficient than using Foreach. Foreach
// forces the creation of an array subset enum each time we
// enumerate
for(int i = 0; i < controlsCollection.Count; i++) {
xClearAllPreferredSizeCaches(controlsCollection[i]);
}
}
/// xGetPreferredSizeCache
/// This value is the cached result of the return value from
/// a control's GetPreferredSizeCore implementation when asked
/// for a constraining value of LayoutUtils.MaxValue (or Size.Empty too).
internal static Size xGetPreferredSizeCache(IArrangedElement element) {
bool found;
Size size = element.Properties.GetSize(_preferredSizeCacheProperty, out found);
if (found && (size != LayoutUtils.InvalidSize)) {
return size;
}
return Size.Empty;
}
/// xSetPreferredSizeCache
/// Sets a control's preferred size. See xGetPreferredSizeCache.
internal static void xSetPreferredSizeCache(IArrangedElement element, Size value) {
Debug.Assert(value == Size.Empty || value != xGetPreferredSizeCache(element), "PERF: Caller should guard against setting PreferredSizeCache to original value.");
#if DEBUG
Debug_SnapProperties(element);
#endif
element.Properties.SetSize(_preferredSizeCacheProperty, value);
Debug.Assert(xGetPreferredSizeCache(element) == value, "Error detected in xGetPreferredSizeCache.");
}
#endregion
#region DockAndAnchorLayoutSpecific
/// GetAutoSizeMode
/// Returns whether or not a control should snap to its smallest size
/// or retain its original size and only grow if the preferred size is larger.
/// We tried not having GrowOnly as the default, but it becomes difficult
/// to design panels or have Buttons maintain their default size of 100,23
internal static AutoSizeMode GetAutoSizeMode(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
return state[_autoSizeModeSection] == 0 ? AutoSizeMode.GrowOnly : AutoSizeMode.GrowAndShrink;
}
/// GetNeedsDockAndAnchorLayout
/// Do not use. Internal property for DockAndAnchor layout.
/// Returns true if DefaultLayout needs to do any work for this element.
/// (Returns false if the element is purely absolutely positioned)
internal static bool GetNeedsDockAndAnchorLayout(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
bool result = state[_dockAndAnchorNeedsLayoutSection] != 0;
Debug.Assert(
(xGetAnchor(element) == DefaultAnchor
&& xGetDock(element) == DefaultDock
&& GetAutoSize(element) == DefaultAutoSize) != result,
"Individual values of Anchor/Dock/AutoRelocate/Autosize contradict GetNeedsDockAndAnchorLayout().");
return result;
}
/// GetNeedsAnchorLayout
/// Do not use. Internal property for DockAndAnchor layout.
/// Returns true if DefaultLayout needs to do anchoring for this element.
internal static bool GetNeedsAnchorLayout(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
bool result = (state[_dockAndAnchorNeedsLayoutSection] != 0) && (state[_dockModeSection] == (int) DockAnchorMode.Anchor);
Debug.Assert(
(xGetAnchor(element) != DefaultAnchor
|| (GetAutoSize(element) != DefaultAutoSize && xGetDock(element) == DockStyle.None)) == result,
"Individual values of Anchor/Dock/AutoRelocate/Autosize contradict GetNeedsAnchorLayout().");
return result;
}
/// GetNeedsDockLayout
/// Do not use. Internal property for DockAndAnchor layout.
/// Returns true if DefaultLayout needs to do docking for this element.
internal static bool GetNeedsDockLayout(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
bool result = state[_dockModeSection] == (int) DockAnchorMode.Dock && element.ParticipatesInLayout;
Debug.Assert(((xGetDock(element) != DockStyle.None) && element.ParticipatesInLayout) == result,
"Error detected in GetNeedsDockLayout().");
return result;
}
/// GetSelfAutoSize
/// Compat flag for controls that previously sized themselves.
/// Some controls rolled their own implementation of AutoSize in V1 for Dock & Anchor
/// In V2, the LayoutEngine is the one responsible for sizing the child items when
/// they're AutoSized. For new layout engines, the controls will let the layout engine
/// size them, but for DefaultLayout, they're left to size themselves.
internal static bool GetSelfAutoSizeInDefaultLayout(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
int value = state[_selfAutoSizingSection];
return (value == 1);
}
/// SetAutoSizeMode
/// Returns whether or not a control should snap to its smallest size
/// or retain its original size and only grow if the preferred size is larger.
/// We tried not having GrowOnly as the default, but it becomes difficult
/// to design panels or have Buttons maintain their default size of 100,23
internal static void SetAutoSizeMode(IArrangedElement element, AutoSizeMode mode) {
BitVector32 state = GetLayoutState(element);
state[_autoSizeModeSection] = mode == AutoSizeMode.GrowAndShrink ? 1 : 0;
SetLayoutState(element, state);
}
/// ShouldSelfSize
/// Compat flag for controls that previously sized themselves.
/// See GetSelfAutoSize comments.
internal static bool ShouldSelfSize(IArrangedElement element) {
if (GetAutoSize(element)) {
// check for legacy layout engine
if (element.Container is Control) {
if (((Control)element.Container).LayoutEngine is DefaultLayout) {
return GetSelfAutoSizeInDefaultLayout(element);
}
}
// else
// - unknown element type
// - new LayoutEngine which should set the size to the preferredSize anyways.
return false;
}
// autosize false things should selfsize.
return true;
}
/// SetSelfAutoSizeInDefaultLayout
/// Compat flag for controls that previously sized themselves.
/// See GetSelfAutoSize comments.
internal static void SetSelfAutoSizeInDefaultLayout(IArrangedElement element, bool value) {
Debug.Assert(value != GetSelfAutoSizeInDefaultLayout(element), "PERF: Caller should guard against setting AutoSize to original value.");
BitVector32 state = GetLayoutState(element);
state[_selfAutoSizingSection] = value ? 1 : 0;
SetLayoutState(element, state);
Debug.Assert(GetSelfAutoSizeInDefaultLayout(element) == value, "Error detected setting AutoSize.");
}
/// xGetAnchor -
/// Do not use this. Use DefaultLayout.GetAnchor.
/// NOTE that Dock and Anchor are exclusive, so we store their enums in the same section.
internal static AnchorStyles xGetAnchor(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
AnchorStyles value = (AnchorStyles) state[_dockAndAnchorSection];
DockAnchorMode mode = (DockAnchorMode) state[_dockModeSection];
// If we are docked, or if it the value is 0, we return DefaultAnchor
value = mode == DockAnchorMode.Anchor ? xTranslateAnchorValue(value) : DefaultAnchor;
Debug.Assert(mode == DockAnchorMode.Anchor || value == DefaultAnchor, "xGetAnchor needs to return DefaultAnchor when docked.");
return value;
}
/// xGetAutoSizedAndAnchored -
/// Do not use. Internal property for DockAndAnchor layout.
/// Returns true if the element is both AutoSized and Anchored.
internal static bool xGetAutoSizedAndAnchored(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
if (state[_selfAutoSizingSection] != 0) {
return false;
}
bool result = (state[_autoSizeSection] != 0) && (state[_dockModeSection] == (int) DockAnchorMode.Anchor);
Debug.Assert(result == (GetAutoSize(element) && xGetDock(element) == DockStyle.None),
"Error detected in xGetAutoSizeAndAnchored.");
return result;
}
/// xGetDock
/// Do not use this. Use DefaultLayout.GetDock.
/// Note that Dock and Anchor are exclusive, so we store their enums in the same section.
internal static DockStyle xGetDock(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
DockStyle value = (DockStyle) state[_dockAndAnchorSection];
DockAnchorMode mode = (DockAnchorMode) state[_dockModeSection];
// If we are anchored we return DefaultDock
value = mode == DockAnchorMode.Dock ? value : DefaultDock;
Debug.Assert(ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill), "Illegal value returned form xGetDock.");
Debug.Assert(mode == DockAnchorMode.Dock || value == DefaultDock,
"xGetDock needs to return the DefaultDock style when not docked.");
return value;
}
/// xSetAnchor -
/// Do not use this. Use DefaultLayout.SetAnchor.
/// Note that Dock and Anchor are exclusive, so we store their enums in the same section.
internal static void xSetAnchor(IArrangedElement element, AnchorStyles value) {
Debug.Assert(value != xGetAnchor(element), "PERF: Caller should guard against setting Anchor to original value.");
BitVector32 state = GetLayoutState(element);
// We translate DefaultAnchor to zero - see the _dockAndAnchorNeedsLayoutSection section above.
state[_dockAndAnchorSection] = (int) xTranslateAnchorValue(value);
state[_dockModeSection] = (int) DockAnchorMode.Anchor;
SetLayoutState(element, state);
Debug.Assert(xGetAnchor(element) == value, "Error detected setting Anchor.");
Debug.Assert(GetLayoutState(element)[_dockModeSection] == (int) DockAnchorMode.Anchor,
"xSetAnchor did not set mode to Anchor.");
}
/// xSetDock
/// Do not use this. Use DefaultLayout.SetDock.
/// Note that Dock and Anchor are exclusive, so we store their enums in the same section.
internal static void xSetDock(IArrangedElement element, DockStyle value) {
Debug.Assert(value != xGetDock(element), "PERF: Caller should guard against setting Dock to original value.");
Debug.Assert(ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill), "Illegal value passed to xSetDock.");
BitVector32 state = GetLayoutState(element);
state[_dockAndAnchorSection] = (int) value; // See xTranslateAnchorValue for why this works with Dock.None.
state[_dockModeSection] = (int) (value == DockStyle.None ? DockAnchorMode.Anchor : DockAnchorMode.Dock);
SetLayoutState(element, state);
Debug.Assert(xGetDock(element) == value, "Error detected setting Dock.");
Debug.Assert((GetLayoutState(element)[_dockModeSection] == (int) DockAnchorMode.Dock)
== (value != DockStyle.None), "xSetDock set DockMode incorrectly.");
}
/// xTranslateAnchorValue -
/// Helper method for xGetAnchor / xSetAnchor.
/// We store anchor DefualtAnchor as None and vice versa.
/// We either had to do this or map Dock.None to DefaultAnchor (Dock & Anchor share the same section
/// in LayoutState.) Mapping DefaultAnchor to 0 is nicer because we do not need to allocate anything in
/// the PropertyStore to get a 0 back from PropertyStore.GetInteger().
private static AnchorStyles xTranslateAnchorValue(AnchorStyles anchor) {
switch(anchor) {
case AnchorStyles.None:
return DefaultAnchor;
case DefaultAnchor:
return AnchorStyles.None;
}
return anchor;
}
#endregion
#region FlowLayoutSpecific
//
internal static bool GetFlowBreak(IArrangedElement element) {
BitVector32 state = GetLayoutState(element);
int value = state[_flowBreakSection];
return value == 1;
}
/// SetFlowBreak
/// Use FlowLayoutSettings.SetFlowBreak instead.
/// See GetFlowBreak.
internal static void SetFlowBreak(IArrangedElement element, bool value) {
Debug.Assert(value != GetFlowBreak(element), "PERF: Caller should guard against setting FlowBreak to original value.");
BitVector32 state = GetLayoutState(element);
state[_flowBreakSection] = value ? 1 : 0;
SetLayoutState(element, state);
LayoutTransaction.DoLayout(element.Container, element, PropertyNames.FlowBreak);
Debug.Assert(GetFlowBreak(element) == value, "Error detected setitng SetFlowBreak.");
}
#endregion
#region AutoScrollSpecific
/// GetLayoutBounds -
/// This is the size used to determine whether or not we need scrollbars.
///
/// Used if the layoutengine always want to return the same layout bounds regardless
/// of how it lays out. Example is TLP in RTL and LTR. VSWhidbey# 392913
internal static Size GetLayoutBounds(IArrangedElement element) {
bool found;
Size size = element.Properties.GetSize(_layoutBoundsProperty, out found);
if (found) {
return size;
}
return Size.Empty;
}
/// SetLayoutBounds -
/// This is the size used to determine whether or not we need scrollbars.
///
/// The TableLayout engine now calls CommonProperties.SetLayoutBounds when
/// it is done with its layout. The layoutbounds are the total column width
/// and the total row height. ScrollableControl checks if the LayoutBounds
/// has been set in the CommonProperties when it tries to figure out if it
/// should add scrollbars - but only if the layout engine is not the default
/// layout engine. If the bounds has been set, ScrollableControl will use
/// those bounds to check if scrollbars should be added, rather than doing
/// its own magic to figure it out.
internal static void SetLayoutBounds(IArrangedElement element, Size value) {
element.Properties.SetSize(_layoutBoundsProperty, value);
}
/// HasLayoutBounds -
/// Returns whether we have layout bounds stored for this element.
internal static bool HasLayoutBounds(IArrangedElement element) {
bool found;
element.Properties.GetSize(_layoutBoundsProperty, out found);
return found;
}
#endregion
#region InternalCommonPropertiesHelpers
/// GetLayoutState - returns the layout state bit vector from the property store.
/// CAREFUL: this is a copy of the state. You need to SetLayoutState() to save your changes.
///
internal static BitVector32 GetLayoutState(IArrangedElement element) {
return new BitVector32(element.Properties.GetInteger(_layoutStateProperty));
}
internal static void SetLayoutState(IArrangedElement element, BitVector32 state) {
element.Properties.SetInteger(_layoutStateProperty, state.Data);
}
#endregion
#region DebugHelpers
#if DEBUG
internal static readonly TraceSwitch PreferredSize = new TraceSwitch("PreferredSize", "Debug preferred size assertion");
internal static string Debug_GetChangedProperties(IArrangedElement element) {
string diff = "";
if (PreferredSize.TraceVerbose) {
Hashtable propertyHash = element.Properties.GetObject(_lastKnownStateProperty) as Hashtable;
if(propertyHash != null) {
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(element)) {
if (propertyHash.ContainsKey(pd.Name) && (propertyHash[pd.Name].ToString() != pd.Converter.ConvertToString(pd.GetValue(element)))) {
diff += "Prop [ " + pd.Name + "] OLD [" + propertyHash[pd.Name] + "] NEW [" + pd.Converter.ConvertToString(pd.GetValue(element)) + "]\r\n";
}
}
}
}
else {
diff = "For more info, try enabling PreferredSize trace switch";
}
return diff;
}
internal static void Debug_SnapProperties(IArrangedElement element) {
// DEBUG - store off the old state so we can figure out what has changed in a GPS assert
element.Properties.SetObject(_lastKnownStateProperty, Debug_GetCurrentPropertyState(element));
}
internal static void Debug_ClearProperties(IArrangedElement element) {
// DEBUG - clear off the old state so we can figure out what has changed in a GPS assert
element.Properties.SetObject(_lastKnownStateProperty, null);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public static Hashtable Debug_GetCurrentPropertyState(object obj) {
Hashtable propertyHash = new Hashtable();
if (PreferredSize.TraceVerbose) {
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(obj)) {
if (pd.Name == "PreferredSize") {
continue; // avoid accidentally forcing a call to GetPreferredSize
}
try {
if (pd.IsBrowsable && !pd.IsReadOnly && pd.SerializationVisibility != DesignerSerializationVisibility.Hidden) {
propertyHash[pd.Name] = pd.Converter.ConvertToString(pd.GetValue(obj));
}
}
catch {
}
}
}
return propertyHash;
}
#endif
#endregion
}
}
|