When you have complicated UI requirements, sometimes you might forget that all of your controls are initialized in your form’s constructor in Windows Forms. This can lead to dramatic performance problems, especially when COM interop is involved. Take for example the MapPoint integration in TEO 3.0.
In TEO 3.0, rather than just calling out to MapPoint and bringing back an image, there is a live MapPoint instance embedded in a tab that you can work with just as if you were using MapPoint. Drag/zoom/etc. However, MapPoint is an out-of-process COM server which means that when the control is created, MapPoint.exe is launched invisibly in the background to serve this functionality. While I’m proud of the plugin, it’s probably one of those things that you won’t use every time you open a contact window. Yet all that initialization is still done in the forms constructor, contributing several seconds to load times. Or at least it would have been.
If you have lots of complicated user interface components tucked away in a tab or invisible panel, consider using this control wrapper I made called DelayLoadedControl(Of T). To use it, you basically pass it a type of a control (must derive from System.Windows.Forms.Control) and add this in place of your control and none of the construction or initialization will be performed until the DelayLoadedControl becomes visible. What this means in TEO’s case is that MapPoint (and probably other tabs as well) won’t be invoked until that tab is selected, saving you valuable form launch time without complicating your design surface too much. Just stick your controls in a UserControl. The code for the control wrapper is below.
/// <summary>
/// A panel that allows for delay-loaded instantiation of controls by waiting for
/// the time that the panel is first shown.
/// </summary>
/// <remarks>
/// The type that is passed into this control instance must be a control with a
/// default constructor. This constructor will be used to create the control when
/// it is added to the panel. This control is very useful for crowded UI's that
/// take up valuable seconds when a form is constructed, but might not be shown
/// right away such as in a tab page that might never be used. This allows the
/// time spent creating that control to be deferred until the user actually
/// requests the UI.
/// </remarks>
/// <typeparam name="T">Your control's type which must expose a default public
/// constructor.</typeparam>
public class DelayLoadedPanel<T> : Panel where T : Control, new( )
{
/// <summary>
/// Holds the control instance that is hosted inside this control
/// </summary>
private T _innerControl;
/// <summary>
/// Event that is raised when the control is created on the fly inside the
/// delay loaded panel.
/// </summary>
public event EventHandler<ControlCreatedEventArgs<T>> InnerControlCreated;
/// <summary>
/// Initializes a new instance of the <see cref="DelayLoadedPanel"/> class.
/// </summary>
public DelayLoadedPanel( )
{
}
/// <summary>
/// Releases the unmanaged resources used by the <see cref="T:Control"/> and
/// its child controls and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged
/// resources; false to release only unmanaged resources.</param>
protected override void Dispose( bool disposing )
{
if ( IsDisposed )
return;
if ( disposing )
{
if ( _innerControl != null )
{
_innerControl.Dispose( );
_innerControl = null;
} // if
} // if
// Calling base last. This is how I think it should be done anyway.
base.Dispose( disposing );
}
/// <summary>
/// Returns the control that is hosted inside this panel, or null if the
/// control is not yet created.
/// </summary>
public T InnerControl
{
get
{
if ( IsDisposed )
throw new ObjectDisposedException( );
return _innerControl;
}
}
/// <summary>
/// Forces the creation of the inner control even though the panel may
/// not have been displayed yet.
/// </summary>
public void ForceInnerControlCreation( )
{
if ( _innerControl == null )
{
_innerControl = new T( );
_innerControl.Dock = DockStyle.Fill;
Controls.Add( _innerControl );
if ( InnerControlCreated != null )
{
InnerControlCreated( this, new ControlCreatedEventArgs<T>( _innerControl ) );
} // if
_innerControl.Visible = true;
} // if
}
/// <summary>
/// Raises the <see cref="E:Control.VisibleChanged"/> event.
/// </summary>
/// <remarks>
/// Overridden to provide on-demand instantiation of the control type
/// supplied in the
/// </remarks>
/// <param name="e">An <see cref="T:EventArgs"></see> that contains the
/// event data.</param>
protected override void OnVisibleChanged( EventArgs e )
{
base.OnVisibleChanged( e );
if ( Visible )
{
ForceInnerControlCreation( );
} // if
}
} // class
/// <summary>
/// Event arguments passed to the <see cref="E:DelayLoadedPanel.InnerControlCreated"/>
/// event of the <see cref="T:DelayLoadedPanel"/> class.
/// </summary>
public class ControlCreatedEventArgs<T> : EventArgs where T : Control, new( )
{
/// <summary>
/// The control instance that was created.
/// </summary>
private T _innerControl;
/// <summary>
/// Initializes a new instance of the <see cref="T:ControlCreatedEventArgs"/>
/// class.
/// </summary>
/// <param name="innerControl">The inner control that was instantiated.</param>
public ControlCreatedEventArgs( T innerControl )
{
if ( innerControl == null )
throw new ArgumentNullException( "innerControl" );
_innerControl = innerControl;
}
/// <summary>
/// The control that was created inside the <see cref="T:DelayLoadedPanel"/>.
/// </summary>
public T InnerControl
{
get
{
return _innerControl;
}
}
} // class