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?

asked May 12, 2016 at 21:01
\$\endgroup\$

1 Answer 1

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.

answered May 13, 2016 at 10:27
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.