| File: System\Windows\Forms\Layout\LayoutTransaction.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Windows.Forms.Layout; // 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 { private readonly Control? _controlToLayout; private readonly bool _resumeLayout; #if DEBUG private readonly 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 is not 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() { _controlToLayout?.ResumeLayout(_resumeLayout); #if DEBUG Debug.Assert(_controlToLayout is null || _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 default(NullLayoutTransaction); } } public static void DoLayout(IArrangedElement? elementToLayout, IArrangedElement? elementCausingLayout, string? property) { if (elementCausingLayout is not null) { CommonProperties.xClearPreferredSizeCache(elementCausingLayout); if (elementToLayout is not null) { CommonProperties.xClearPreferredSizeCache(elementToLayout); elementToLayout.PerformLayout(elementCausingLayout, property); } } Debug.Assert(elementCausingLayout is not 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 is not null) { CommonProperties.xClearPreferredSizeCache(elementCausingLayout); } } else { DoLayout(elementToLayout, elementCausingLayout, property); } } }