A Generic Singleton Form Provider for C#
An interesting, although generally basic, problem with Windows Forms is how to implement the singleton pattern on the forms. What do I mean? Well, consider this…
You’re writing a Windows Forms application and you have a bunch of Forms, such as a Preference window, an About window and so on. You’d like these forms to be modeless, yet you don’t want the user to be able to create more than one of them.
It’s a common problem, but there’s no built in way to handle it. Moreover, I’ve yet to see an elegant solution to it. The method explained below, which is as elegant as it’s going to get, creates a pseudo-factory class to manage the problem for you. Read on to find out more.
If a Form were just any old object, you could implement the Singleton pattern in the said class:
class SomeSingletonClass {
protected SomeSingletonClass () {
}
private static SomeSingletonClass mInstance = null;
public static SomeSingletonClass Instance {
get {
if (mInstance == null) mInstance = new SomeSingletonClass();
return mInstance;
}
}
}
Of course, this isn’t thread safe – there are much better ways to do this in the general case. See here for a good explanation. In the case of forms, however, we don’t need to be thread safe. Windows forms are explicitly un-threadsafe themselves. They should (in most cases) only ever be initialized/used from a single thread. This constraint applies here too.
Now, this won’t work for Windows Forms. How come? Well, once the Windows Form has been closed (with Close() or otherwise) it, generally, gets disposed. We all know you can’t use a disposed form again. You’ll get a crash when trying to access the form again. So, what’s the work around?
Basically, we need to handle the FormClosed event, so that we know the form has been closed. We can then remove the reference to the form, once its been closed, and start all over again next time it’s needed. So we’d end up with something like this:
class SomeSingletonForm : Form {
protected SomeSingletonForm () {
}
private static SomeSingletonForm mInstance = null;
public static SomeSingletonForm Instance {
get {
if (mInstance == null)
{
mInstance = new SomeSingletonClass();
mInstance.FormClosed += new FormEventHandler(remover);
}
/* Could call mInstance.Show(); */
return mInstance;
}
}
static void remover(object sender, FormClosedEventArgs e)
{
mInstance.FormClosed -= new FormClosedEventHandler(remover);
mInstance = null;
}
}
Now we’re getting closer. It seems unnecessary, however, to implement this on every single form that we want to be a singleton form: It’s a lot of copying identical code, and it’s prone to bugs.
So what can we do? We create a provider class which creates singleton forms for us. Sort of like a singleton factory with a few little tricks to ensure only valid forms are around. I’ve seen code before for this on various blogs / message boards, but none of them are as complete as this one.
What’s the difference? It’s the use of generics that makes this implementation really handy. By using a generic method, we can return a form of the correct type: there’s no need to worry about casting.
Another modification is the addition of a parameters to the GetInstance method. Although this is a little dodge, and could break things if you aren’t careful, I’ve found it handy when used with a form whose constructor required extra arguments.
So here it is:
class SingletonFormProvider
{
static Dictionary<Type, Form> mTypeFormLookup = new Dictionary<Type, Form>();
static public T GetInstance<T>(Form owner)
where T : Form
{
return GetInstance<T>(owner, null);
}
static public T GetInstance<T>(Form owner, params object[] args)
where T : Form
{
if (!mTypeFormLookup.ContainsKey(typeof(T)))
{
Form f = (Form)Activator.CreateInstance(typeof(T), args);
mTypeFormLookup.Add(typeof(T), f);
f.Owner = owner;
f.FormClosed += new FormClosedEventHandler(remover);
}
return (T)mTypeFormLookup[typeof(T)];
}
static void remover(object sender, FormClosedEventArgs e)
{
Form f = sender as Form;
if (f == null) return;
f.FormClosed -= new FormClosedEventHandler(remover);
mTypeFormLookup.Remove(f.GetType());
}
}
and to use it:
AboutBox abox = SingletonFormProvider.GetInstance<AboutBox>(this); abox.Show();
Well, there you have it. A handy Generic Singleton Form provider for C#.
Comment by Pedro — January 15, 2009
Posted at 7:55 pm
Extraordinary!
A little hidden perl. Thanks.
Comment by Ian — January 26, 2009
Posted at 2:09 pm
This is far amd away the most elegannt, concise and correct implememtation and solution to a very common problem. Thank you for sharing it.
Comment by Albert — February 4, 2009
Posted at 7:15 pm
Good implementation.
But, i have a question. I have the following code:
static class Program
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(MenuPrincipalObservador.InitForm());
}
}
class MenuPrincipalObservador
{
private MenuPrincipalObservador()
{
}
public static Form InitForm()
{
MenuPrincipalObservador mpo = new MenuPrincipalObservador();
MDIParent1 mdi = MenuPrincipalFormSingleton.Instance.createForm();
mdi.BtnRecepcioMercaderiesClicked += mpo.ClickRecepcioHandler;
return mdi;
}
private void ClickRecepcioHandler(Object sender, EventArgs e)
{
frmRecepcioMerc frm = SingletonFormProvider.GetInstance(iDontKnow);
frm.Show();
}
}
In the last function ClickRecepcioHandler, like him shipment the owner?
Thanks for your attention.
Comment by Pedro — February 19, 2009
Posted at 7:56 pm
Excellent implementation. Here is the version form Windows Mobile 6. It’s actually working on my current project:
public static class SingletonFormProvider
{
#region Singleton
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static SingletonFormProvider()
{
}
#endregion
static Dictionary mTypeFormLookup = new Dictionary();
static public T GetInstance()
where T : Form
{
return GetInstance(null, new Type[]{ } /*sin parámetros*/);
}
static public T GetInstance(Form owner)
where T : Form
{
return GetInstance(owner, new Type[] { }) /*sin parámetros*/;
}
static public T GetInstance(Form owner, params object[] args)
where T : Form
{
// ¿Ya hay una instancia del tipo T?
if (mTypeFormLookup.ContainsKey(typeof(T)))
{
T f = (T)mTypeFormLookup[typeof(T)];
// ¿Esa instancia está disposed?
if (f.IsDisposed)
{
// Borro para la entrada correspondiente al tipo typeof(T) la instancia (formulario) f, puesto que está disposed.
mTypeFormLookup.Remove(f.GetType());
}
}
// ¿No hay instancia de tipo T?
if (!mTypeFormLookup.ContainsKey(typeof(T)))
{
// Constructor de la clase T con parámetros args.
ConstructorInfo constructorInfo =
typeof(T).GetConstructor(args.Select(p => p.GetType()).ToArray());
Debug.Assert(constructorInfo != null,
string.Format(”La clase {0} no tiene ningún constructor disponible.”,
typeof(T)));
// Equivale a: T f = new T(…, [args], …);
Form f = constructorInfo.Invoke(args) as T;
#if DEBUG
if (f == null)
{
object[] obj = constructorInfo.GetParameters().Select(p => p.Name).Cast().ToArray();
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (string param in constructorInfo.GetParameters().Select(p => p.Name))
{
sb.Append(param);
sb.Append(”=”);
sb.Append(args[i++]);
if (i < args.Length) sb.Append(”, “);
}
Debug.Assert(f != null,
string.Format(”No se ha podido invocar el constructor {0} de la clase {1} con parámetros ({2}).”,
constructorInfo.Name, typeof(T), sb.ToString()));
}
#endif
// Añado para la entrada correspondiente al tipo typeof(T) la instancia (formulario) f.
mTypeFormLookup.Add(typeof(T), f);
f.Owner = owner;
}
return (T)mTypeFormLookup[typeof(T)];
}
}
Comment by Nuno Coimbra — May 16, 2009
Posted at 10:21 pm
HEllo!
This is cool! But it doesn’t work in .NET 3.5 (C#). Keeps saying that the constructor of SomeSingletonForm can’t be found
It only works with a public constructor. Am I doing something worong?
Comment by Nuno Coimbra — May 16, 2009
Posted at 10:33 pm
oh, and another thing… I’ve tested this an looked at it, and found out that the Forms don’t have to be Singleton objects for the provider to work.
Comment by David Hammerton — May 17, 2009
Posted at 8:34 am
Hi Nuno,
There isn’t any reason for it not to work under .NET 3.5, that I can see. What’s the exact problem you’re seeing?
You are correct, you don’t inherit from the Singleton object, your Forms shouldn’t be ‘Singleton objects’. You simply use the class’ GetInstance method to create singleton instances of forms for you.
Feel free to paste your code if you are having trouble.
David
Comment by Nuno Coimbra — May 18, 2009
Posted at 7:09 pm
Hello David!
I’m know for, sometimes, miss the tinny picture, i.e., the details… By rushing into things without proper first reading.
My code is exactly the one here.
I’ve created a new C# project, copy and pasted your code into it and run it. In fact I was surprised it didn’t work, since I’ve seen no reason for it to fail… Until now. Now I see what I’ve missed. :S It’s supposed to be used with regular forms… duh!
Thanks! As I said before, this is truly a cool implementation and it can provide me a way for getting rid of all my Singleton forms… I’ll put it to a good test.
Comment by Qifogok — March 4, 2010
Posted at 7:42 pm
Watch how ortho evra pain at patch site the dilemma climara patch side effects lest she lactate and post-exercise recovery the gardener potassium manganese medical that could storz fittings undania can sterile o ring syringes virtually complete hydrocodone bitartrate cough syrup for similar free florinef cross wind scrubs season 2 your clothing complement activation now looked psa dna coa auto baseball also true elbow lump swellling redness grappling hooks phendimetrazine indeed joy barry sanders workout regimen survive dis continuous erythropoietin receptor activator online mere threat 110 volt ac tig welder kit booted the quinine hydrochloride growled assent prempro no prescription ing nodded rhino brush hogs jewel set side effects of maalox the fallen aluminium silicon bismuth alloys uk been even pg county harbor discovered that lotrimin liquid had little advanced prostate cancer and rib pain make much false positive tuberculin skin tests rlene and hair acid pantothenic dry mineral have decided how to make ammonia citrate dismiss them melt bisacodyl suppositories twist one cx cx2 setup guide hou shallst atlanta rezulin lawyers routine observatio prescription eyewear for hiking apart and generic sevoflurane minrad and stairs froggie tales lambs ivy rattled around how to make smelling salts hey crossed ceiling fans sone and cfm rating crack under vegan ergocalciferol adult human david tate’s samantha mule attention passed beginning klonopin could pick velcade for maximum progesterone pill massive ogre westrim crafts homepage always thought tagamet for canine colitis bird form sucrose acetate isobutyrate side passage ppa safe harbor minimum coverage head under allergies bacitracin the duties 301 alora court san ramon ca any magic methionine grams in rumen pitched voice bpw suspension china quite place does arnica work comb and yeast infection in late pregnancy sides seemed troth.
Comment by Sudhi — March 5, 2010
Posted at 12:31 pm
Thank you for Nice example