Sometimes, ensuring that there exists one and only one instance of a Control on any given Page may be a good idea. A great example of this is the WebPartManager control that you need to place on any page that uses WebParts - or the ScriptManager control that you need in order to use ASP.NET Ajax. Omit them and you'll get an exception stating that its missing - add more than one and you'll get an exception saying that you can only have one.
If you've ever wondered how to implement such a control, here's an example - which is essentially similar to how the WebPart and ScriptManagers are implemented:
public class MyManager : Control
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
Page page = this.Page;
if (page != null)
{
// check if an instance has already been added to the page
if (page.Items.Contains(typeof(MyManager)))
{
throw new InvalidOperationException("Only one instance of the MyManager control can be placed on a Page");
}
// add a reference to the Items collection for easy reference to the control; GetCurrent() will use this
page.Items[typeof(MyManager)] = this;
}
}
/// <summary>
/// Gets the current MyManger instance on a given Page
/// </summary>
/// <param name="page">The page</param>
/// <returns>The MyManager control</returns>
/// <exception cref="InvalidOperationException">If no MyManager control exists on the page</exception>
public static MyManager GetCurrent(Page page)
{
if (null == page)
{
throw new ArgumentNullException("page");
}
// get the manager - remember we added it to the Items collection in OnInit
MyManager manager = page.Items[typeof(MyManager)] as MyManager;
if (null == manager)
{
// no manager on Page - crash and burn
throw new InvalidOperationException("Could not find a MyManager control on the page");
}
return manager;
}
}
Implemented like this, the first MyManager control that gets added to a page stores a refrence to itself in the page context. The page context (the Page.Items property) is basically an IDictionary that has the same lifetime as the page, so its ideal for storing things that you'll need later on in the page life cycle. Once the first control has been added, any subsequent controls that tries to add itself will throw an exception alerting the user that this is not allowed.
Now, when we need to get at the current MyManger control on a page, we can do so at any time in the page lifetime after the Init stage using the static GetCurrent method. In the above implementation this will throw an exception if the control has not been added - wheter you want to do this instead of just returning null depends on how you intend to use the control.