3
\$\begingroup\$

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:

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

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

    /// <summary>Calls ResumeLayout on the container.</summary>
    /// <remarks>If you call this manually, you are probably using this class wrong.</remarks>
    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

  1. Is there any reason not to use an IDisposable context here? I'm more familiar with , where I wouldn't hesitate to create new context managers - but the method name Dispose() raises red flags. (In python, it's __exit__.)

  2. I debated between the extension method on Control and a static method on SuspendLayoutContext:

    public static SuspendLayoutContext For(Control container, bool performLayoutAfter=true)
    {
        return new SuspendLayoutContext(container, performLayoutAfter)
    }
    

    While I'm a big fan of fluent interfaces, the extension method felt cleaner. What alternatives might be better?

  3. I always miss something when I do this, what is it this time?

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.