MDI, check if a form already exists

Fhwoarang

Newcomer
Joined
Aug 31, 2008
Messages
24
Hello, I'm new to this forum.

I'm kinda new to .NET, but I like it a lot. I'm working on a program that controls the rents payment in a building. So I decided it should have several modules: tenants, departments, rent, tax, and a "generate new payment" module, in case they have to pay for something else.

Anyway, I decided to use the MDI interface. I'm learning from a book I bought, so I don't know if there are other ways to do what I want to do. The problem is each module comes from one form, and there should be only ONE form of each module. The book has the following code, in C#:

Code:
//ChildForm is the name of an existing form, intented to be the model of all child forms
ChildForm NewChildForm;
NewChildForm = new ChildForm();
NewChildForm.Text = "Form" + MdiChildren.Length.ToString();
//The next line converts the form into a child form
NewChildForm.MdiParent = this;
NewChildForm.Show();

This code is supposedly used on a menu, on "File", "New". So, you generate copies of the child form.

What I need is to only be able to generate one form and that's it. I don't know if there is a way to check if a class is already instantiated before you create an object of it (in this case, before you create a NewChildForm object, I would like to know if there is an already oppened or created ChildForm active).

I my mind, or my idea, is to use buttons that call each module: one button for rents, one button for departments, one button for tenants, etc. But each form is unique, so I don't want each button to generate copies of each form. Just open it or do nothing if it's already oppened (maybe just use the Show method again if it already exists).

Maybe the MDI is not the best way to do this, so I want to hear your oppinions.

Thanks in advice.
 
MDI is fine, a tabbed look like visual studio or something similar to Outlook with the options down the side might have a more modern look. If you have the time http://msdn.microsoft.com/en-us/library/aa480450.aspx is a very useful tool when developing these kind of applications (the tutorial included is pretty comprehensive as well).

Getting to your original question of how to prevent duplicate instances however...

One way to do this is prevent anything but the form itself creating an instance and let it manage the creation logic. Something like the following should give you an idea.

C#:
//modified child form
    public partial class ChildForm : Form
    {
        private ChildForm()  //prevents form being created from anywhere else.
        {
            InitializeComponent();
        }

        static private ChildForm _instance;
        
        static public ChildForm Instance
        {
            get
            {
                if(_instance == null)
                    _instance = new ChildForm();

                return _instance;
            }
        }
    }

C#:
//in the creating form use the following to get to the instance
ChildForm f = ChildForm.Instance;
 
MDI is fine

Man, that worked beautiful. :D Thank you very much.

I'm not very experienced with object oriented programming and so with .NET, but I understand its basics, but I lack a lot of experience. I usually use VB6. I think I won't use any additional programs, like the one you mentioned, until I understand why I need to use it or know about .NET limitations. But I thank you anyway for your suggestion.

As for the code you gave me, could you explain it to me a little?:D Basically, I understood everyline you wrote. But I never would have come up with that by my self.

So, let me explain the code and tell me if I'm grong in anything.

C#:
private ChildForm()  //prevents form being created from anywhere else.
        {
            InitializeComponent();
        }

So, basically, you changed the access level to "private", but I still managed to call a ChildForm from the PartentForm (using the property you made). So I really don't know, in this case, the difference beetwen "private" and "public".

C#:
static private ChildForm _instance;

That's an attribute, I think, or a type ChildForm variable meant to be used to know if an instance already exists or not.

C#:
        static public ChildForm Instance
        {
            get
            {
                if(_instance == null)
                    _instance = new ChildForm();

                return _instance;
            }
        }

This is a property, it's not a method. I know it because you are using "get" and because there is not parenthesis on the declaration. What I don't fully understand is why this property results in a windows form. Maybe because _instace = new ChildForm(); But I feel I don't have not completely understand it.

Anyway, everything worked great. I just needed to declarate the ChildForm f variable as MdiParent = this and then use _instance.Show(); to make the form appear on the screen.

I think the weird thing here is that you are using an attribute to create an object, where usually objects come from a random name or different places.

Is like if I have this class, for instance:

C#:
public class Dog {

public string walk;
public string bark;
}

And then you do this:
C#:
bark = new Dog();

I don't know, it's just weird for me, but it did the job. Maybe it has something to do with the fact it was "static", but I don't know. :)
 
Last edited:
Code:

private ChildForm() //prevents form being created from anywhere else.
{
InitializeComponent();
}


So, basically, you changed the access level to "private", but I still managed to call a ChildForm from the PartentForm (using the property you made). So I really don't know, in this case, the difference beetwen "private" and "public".
pretty much spot on - making the constructor private prevents code outside of the ChildForm class calling it. Note the ChildForm class itself can still call this though.

C#:
static private ChildForm _instance;
just defines a private variable - again only accessible to the ChildForm class. The keyword static means that only one single instance of this variable exists in memory. If you have multiple instances of the ChildForm class they would all share this variable. In fact this exists without any actual instance of the ChildForm class.

That's an attribute, I think, or a type ChildForm variable meant to be used to know if an instance already exists or not.

Code:

static public ChildForm Instance
{
get
{
if(_instance == null)
_instance = new ChildForm();

return _instance;
}
}


This is a property, it's not a method. I know it because you are using "get" and because there is not parenthesis on the declaration. What I don't fully understand is why this property results in a windows form. Maybe because _instace = new ChildForm(); But I feel I don't have not completely understand it.

Pretty much spot on again. The code defines a read only property (a get but no set) that is public so it can be called from anywhere. Again the keyword 'static' means this exists at the class level and doesn't require an instance of the class to be created to call it. The return type is a ChildForm because it will return a valid instance of itself.

All the property get does is delay the actual form creation until it is needed - if we have never accessed this property before then _instance is null so the Childform creates a new instance (it can call it's own private constructor) and stores it into the _instance field and then returns this to the calling code.


And then you do this:
Code:

bark = new Dog();

If you step through the code you will see it still does something similar, it is just now internal to the ChildForm class and hidden behind the call to ChildForm.Instance
 
Hey, man, something is wrong. Everytime I close the child form and try to reopen it with the same button that oppened it for the first time, an error ocurrs and says that I'm trying to use an eliminated object.

I don't get it. :confused: :eek:

I guess this has something to do with the object destructor, and I should get rid of the close button and create a new one that only hides the form, instead closing it. It wouldn't make sense if I add a destructor method creating a new object, which would bring back the problem I was trying to solve from the beggining.
 
Last edited:
Seems like adding this code fixes the problem.

C#:
private void ChildForm_FormClosed(object sender, FormClosedEventArgs e)
{
instancia = new ChildForm();
}

I guess it doesn't matter if I'm creating a new object, at least they can't open two windows at the same time (which was the initial problem of creating multiple objects), and they can reopen it after closing it.

Please, somebody advice me if this is a bad idea, because I don't know if this will give me troubles, since I will add data to the form and use with a database and such.
 
Last edited:
C#:
static public ChildForm Instance
    {
        get
        {
            if (_instance == null || _instance.IsDisposed)
                _instance = new ChildForm();

            return _instance;
        }
might be a better way of doing my original code - it now checks to see if the form is either null or has been previously closed / disposed.
 
might be a better way of doing my original code - it now checks to see if the form is either null or has been previously closed / disposed.
Plausibly:That's a neat idea, and I've expanded on the concept in a custom form with a progress bar for a worker thread.
Code:
public static ProgressForm Instance {
  get {
    if ((_instance == null) || (_instance.IsDisposed == true) || (_instance.Cancelled == true)) {
      _instance = new ProgressForm();
    //} else {
    //  _instance.BringToFront(); // this is used for Mdi Children
    //  _instance.Focus(); // this is used for Mdi Children
    }
    return _instance;
  }
}
Notice I had to include a check for _instance.Cancelled. This is because I call my progress form modally:
Code:
// ...Initialize, etc.
bgWorker.RunWorkerAsync();
if (bgWorker.IsBusy == true) {
  m_frmProgress = ProgressForm.Instance;
  try {
    m_frmProgress.ShowDialog();
    m_frmProgress.Dispose();
  } catch (Exception err) {
    Console.WriteLine(err.Message);
  } finally {
    m_frmProgress = null;
  }
}
Both above (in the creation) and below (when the thread completes) I have coded a way to close the modal progress form:
Code:
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
  if (m_frmProgress != null) {
    try {
      m_frmProgress.Close();
    } catch (Exception err) {
      Console.WriteLine(err.Message);
    } finally {
      m_frmProgress = null;
    }
  }
}
The part about Closing the Progress Form and distroying it in both places seems redundant, but I don't know where the best place is for ensuring the form goes away after the thread has stopped.

Any suggestions?
 
Back
Top