HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Context manager for SuspendLayout

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
contextsuspendlayoutformanager

Problem

I've found a few places where control updates take longer than the redraw cycle.

I started off surrounding them with Suspend and ResumeLayout:

container.SuspendLayout(); 
updateControls(); 
container.ResumeLayout(true);


After doing that a few times, I realized it could be refactored into a context:

/// 
/// Context manager to suspend and resume layout engine while performing 
/// updates that take longer than a refresh cycle
/// 
/// using (new SuspendLayoutContext(container)
///     DoSlowUpdate(container.Controls);
class SuspendLayoutContext : IDisposable
{
    private readonly Control _control;
    private readonly bool _performLayoutAfter;

    /// 
    /// Constructs the context manager to suspend and resume layout.
    /// 
    /// 
    /// 
    public SuspendLayoutContext(Control container, bool performLayoutAfter=true)
    {
        _control = container;
        _performLayoutAfter = performLayoutAfter;
        _control.SuspendLayout();
    }

    /// Calls ResumeLayout on the container.
    /// If you call this manually, you are probably using this class wrong.
    public void Dispose()
    {
        _control.ResumeLayout(_performLayoutAfter);
    }
}


Then, the above code would look like

using (new SuspendLayoutContext(container))
{
    updateControls(); 
}


I then decided I didn't like having to call the constructor every time, so I created an extension method (/// comments elided for brevity):

static class ControlExtensions
{
    public static SuspendLayoutContext SuspendLayoutContext(this Control container, bool performLayoutAfter=true)
    {
        return new SuspendLayoutContext(container, performLayoutAfter);
    }
}


The example then looks like

using (container.SuspendLayoutContext())
{
    updateControls(); 
}


Questions

-
Is there any reason not to use an IDisposable context here? I'm more familiar with python, where I wouldn't hesitate to create new context managers - but the method name

Solution

I think there is nothing wrong with using IDisposable whenever a class requires some sort of clean-up. It might have been originally designed to release unmanaged resources, but it is definitely no longer the case. Nowadays people use IDisposable to do all sort of things: unsubscribe from events, release pooled objects, flush buffers... this list goes on and on.

I also would prefer extension method over other options. It looks cleaner IMHO. What I don't like is the Context word. Not only because the method name should be a verb, but also because its somewhat misleading. I think a lot of people will assume that ...Context class is the class that implements context object (anti-)pattern. But your class doesn't. For example, class LayoutSuspender both sounds funny and describes what the class does. Win-win.

The thing you should be careful about is using this class with nested methods. For example, this:

public void UpdateAllControls()
{
    using (container.SuspendLayoutContext())
    {
        UpdateControlA(); 
        UpdateControlB(); 
    }
} 

public void UpdateControlA()
{
    using (container.SuspendLayoutContext())
    {
        UpdateControlA(); 
    }
}


won't work as you want it to. So eventually you might want to implement some sort of reference counting.

Code Snippets

public void UpdateAllControls()
{
    using (container.SuspendLayoutContext())
    {
        UpdateControlA(); 
        UpdateControlB(); 
    }
} 

public void UpdateControlA()
{
    using (container.SuspendLayoutContext())
    {
        UpdateControlA(); 
    }
}

Context

StackExchange Code Review Q#128222, answer score: 2

Revisions (0)

No revisions yet.