How to avoid duplicate form creation in .NET Windows Forms? - c#

I am using .NET Windows Forms. My MDI parent form contains the menu. If click the menu the form will be displayed. Up to now no problem.
UserForm uf = new UserForm();
uf.Show();
uf.MdiParent = this;
If I click the menu again another duplicate of the form is created. How to solve this issue?

The cleanest way is to simply track the lifetime of the form instance. Do so by subscribing the FormClosed event. For example:
private UserForm userFormInstance;
private void showUserForm_Click(object sender, EventArgs e) {
if (userFormInstance != null) {
userFormInstance.WindowState = FormWindowState.Normal;
userFormInstance.Focus();
}
else {
userFormInstance = new UserForm();
userFormInstance.MdiParent = this;
userFormInstance.FormClosed += (o, ea) => userFormInstance = null;
userFormInstance.Show();
}
}

You should create a singleton class for managing your form instances:
public class FormProvider
{
public static UserForm UserForm
{
get
{
if (_userForm== null || _userForm.IsDisposed)
{
_userForm= new UserForm ();
}
return _userForm;
}
}
private static UserForm _userForm;
}
NB, this is a very simple Singleton pattern. For the correct way to use the pattern, use this link.
You can then just access the form as follows:
FormProvider.UserForm.Show();
FormProvider.UserForm.MdiParent = this;
When FormProvider.UserForm is accessed for the FIRST time, it will be created. Any subsequent get on the FormProvider.UserForm property will return the form that was created on first access. This means that the form will only ever be created once.

In contrast to the existing answers here, I would not recommend using a Singleton for this. The Singleton pattern is woefully overused, and is generally a "code smell" that indicates that something's gone wrong with your overall design. Singletons are generally put in the same "bucket" as global variables: you'd better have a really strong case for using it.
The simplest solution is to make an instance variable on your main form that represents the form in question, then use that to show it.
public class MainMdiForm : Form
{
...
UserForm userForm;
...
private void ShowUserForm()
{
if(userForm == null || userForm.IsDisposed)
{
userForm = new UserForm();
userForm.MdiParent = this;
}
userForm.Show();
userForm.BringToFront();
}
}

If you know the name of the form :
if (Application.OpenForms["FormName"] == null)
{
Form form = new Form();
form.MdiParent = this;
form.Show();
}
else
Application.OpenForms["FormName"].Focus();

Options:
make UserForm a singleton: http://msdn.microsoft.com/en-us/library/ff650316.aspx
disable the button or menu item when it is clicked: menuItem.Enabled = false;
Typically, disabling the button works fine, and makes more sense from the user's perspective. Singleton works if you need the button enabled for something else.
Singleton is probably not a good solution if the form could be closed and a new instance will later be required.

You could always make the Form a Singleton:
public class MyForm : Form
{
private MyForm _instance = null;
private object _lock = new object();
private MyForm() { }
public static MyForm Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
_instance = new MyForm();
}
}
return _instance;
}
}
}
Then your call would look something like:
MyForm.Instance.Show();
MyForm.Instance.MdiParent = this;

You could just examine the MdiChildren property of your host form to determine if an instance of your UserForm exists in it.
UserForm myForm = null;
foreach (Form existingForm in this.MdiChildren)
{
myForm = existingForm as UserForm;
if (myForm != null)
break;
}
if (myForm == null)
{
myForm = new UserForm();
myForm.MdiParent = this;
myForm.Show();
}
else
myForm.Activate();
This will create a new instance of your UserForm is it doesn't already exist, and it will switch to the created instance if it does exist.

This is my solution in ShowForm() and calling sample in aboutToolStripMenuItem_Click():
private void ShowForm(Type typeofForm, string sCaption)
{
Form fOpen = GetOpenForm(typeofForm);
Form fNew = fOpen;
if (fNew == null)
fNew = (Form)CreateNewInstanceOfType(typeofForm);
else
if (fNew.IsDisposed)
fNew = (Form)CreateNewInstanceOfType(typeofForm);
if (fOpen == null)
{
fNew.Text = sCaption;
fNew.ControlBox = true;
fNew.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
fNew.MaximizeBox = false;
fNew.MinimizeBox = false;
// for MdiParent
//if (f1.MdiParent == null)
// f1.MdiParent = CProject.mFMain;
fNew.StartPosition = FormStartPosition.Manual;
fNew.Left = 0;
fNew.Top = 0;
ShowMsg("Ready");
}
fNew.Show();
fNew.Focus();
}
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
ShowForm(typeof(FAboutBox), "About");
}
private Form GetOpenForm(Type typeofForm)
{
FormCollection fc = Application.OpenForms;
foreach (Form f1 in fc)
if (f1.GetType() == typeofForm)
return f1;
return null;
}
private object CreateNewInstanceOfType(Type typeofAny)
{
return Activator.CreateInstance(typeofAny);
}
public void ShowMsg(string sMsg)
{
lblStatus.Text = sMsg;
if (lblStatus.ForeColor != SystemColors.ControlText)
lblStatus.ForeColor = SystemColors.ControlText;
}
public void ShowError(string sMsg)
{
lblStatus.Text = sMsg;
if (lblStatus.ForeColor != Color.Red)
lblStatus.ForeColor = Color.Red;
}

Related

how to call a method or function inside MDI main parent I need to instantiate a form on class and new instance parent shoud mainparent?

//MdiParent mainparent.cs
public static void lodforweb()
{
frm_webcs frmload_webcs = new frm_web
{
MdiParent = this
};
frmload_webcs.Show();
}
//Context menu class
//Cl_contextmenu.cs
public bool OnContextMenuCommand()
{
if (commandId == (2020)
{
mainparent.lodforweb();
return true;
}
}
}
// having a problem with "this" using static method
// instantiating does not work also.
From the comments:
once I right click the child form context menu popups. I want to
generate another child form and should be child of mainparent
Also provided from the comments:
private void childform_Load(object sender, EventArgs e) {
cl_chromebrowser urload = new cl_chromebrowser();
panel1.Controls.Add(urload.choniumeload("www.site.com"));
urload.chromebrowser.MenuHandler= new cl_contexmenu();
}
So your child form is called "childform". Add a static member that holds a reference to the MDI parent, and set it in the Load() event:
public static Form MyMdiParent;
private void childform_Load(object sender, EventArgs e)
{
// ... your existing code from above ...
childform.MyMdiParent = this.MdiParent;
}
Now you can use that MDI parent directly from your context menu:
public bool OnContextMenuCommand()
{
if (commandId == 2020)
{
if (childform.MyMdiParent != null)
{
childform.MyMdiParent.Invoke((MethodInvoker)delegate ()
{
frm_webcs frmload_webcs = new frm_webcs();
frmload_webcs.MdiParent = childform.MyMdiParent;
frmload_webcs.Show();
});
}
}
return true;
}

How to create a common function for show form?

I usually used the following code to show form:
frmEmployeeManage em = null;
private void ShowEmployee_Click(object sender, EventArgs e)
{
if (em == null || em.IsDisposed)
{
em = new frmEmployeeManage();
em.MdiParent = this;
em.FormBorderStyle = FormBorderStyle.None;
em.WindowState = FormWindowState.Maximized;
em.Show();
}
else
{
em.Activate();
}
}
Now I want to write a function for showing form. The following code I don't know how to pass a form class as parameter to the function.
class CommonService
{
public static void ShowFrom(Form frmChild, Form frmParent)
{
if (frmChild == null || frmParent.IsDisposed)
{
frmChild = new Form(); // How passing the form class here?
frmChild.MdiParent = frmParent;
frmChild.FormBorderStyle = FormBorderStyle.None;
frmChild.WindowState = FormWindowState.Maximized;
frmChild.Show();
}
else
{
frmParent.Activate();
}
}
}
Finally I use the show form function like the following example:
frmEmployeeManage em = null;
CommonService.ShowForm(frmEmployee, this);
I think what you need is to use a ref parameter:
public static void ShowFrom<T>(ref T frmChild, Form frmParent) where T : Form, new()
{
if (frmChild == null || frmParent.IsDisposed)
{
frmChild = new T(); // How passing the form class here?
frmChild.MdiParent = frmParent;
frmChild.FormBorderStyle = FormBorderStyle.None;
frmChild.WindowState = FormWindowState.Maximized;
frmChild.Show();
}
else
{
frmParent.Activate();
}
}
And call it like this:
frmEmployeeManage em = null;
CommonService.ShowForm(ref em, this);
ref allows you to change the value of the parameter in a method, and the changes are reflected on the variable passed in as well.

Create and show a Form instance based on a boolean using Generic Method

I have these classes :
internal partial class FBase : Form
{
public FBase() { InitializeComponent(); }
public FBase(bool owner) : this()
{
if (!owner) { this.Opacity = 0; Load += (s, e) => Close(); }
}
}
internal partial class Form1 : FBase
{
public Form1(bool owner) : base(owner) { InitializeComponent(); }
}
This code works (does not show Form1):
Form1 f = new Form1(false);
if(f != null) { f.MdiParent = parent; f.Show(); }
But this does not work (shows Form1):
OpenSingleMdiChild(() => new Form1(false));
This is the implementation of OpenSingleMdiChild:
public static void OpenSingleMdiChild<T>(this Form parent, Func<T> factory) where T : Form
{
T f = null;
foreach (Form c in parent.MdiChildren) if (c.GetType() == typeof(T)) { f = c; break; }
if (f == null) { f = factory(); f.MdiParent = parent; }
f.Show(); f.Select();
}
If I understand your question, you want to know why Form1 isn't shown. I think your FBase Constructor is the reason:
public FBase(bool owner) : this()
{
if (!owner) { this.Opacity = 0; Load += (s, e) => Close(); }
}
There you tell the Load-Event to Close(); the Form. So it will directly be closed if owner is false. Further you should use some returns in your OpenSingleMdiChild-Method. Because this is really hard to read as #PeterBons points out.
Furthermore this code isn't very clear to me:
using (Form1 f = new Form1(false))
{
if(f != null) { f.MdiParent = parent; f.Show(); }
}
You show a Form and directly destroy it? Why should someone do this?
I hope it answers your questions. Else ask some real questions please ;).

Using ArrayList from different class with out creating new instance of the class

I had similar problem few days earlier but now is more complicated.
I am trying to use ArrayList from form 2, in form 1. I can not create a new instance of form 2 because it will make my content of form 2 null. How could I do this by changing my piece of code?
Examples for dummies recommended.
Edit
int totalEntries = 0;
var myarr = SharedResources.Instance.ArrayList;
if(cmbType.SelectedIndex == 0)
myarr.Add(new AddPC(cmbType.Text,
txtUserName.Text, txtPassword.Text));
Why would above code cause nullreferenceexception?
private void sfdSaveToLocation_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
{
EncryptDecrypt en = new EncryptDecrypt();
StringBuilder sb = new StringBuilder();
// I need the arraylist here but I can not access it as it is.
foreach (var list in addWindow.addedEntry)
{
if (list is AddPC)
{
AddPC tmp = (AddPC)list;
sb.Append(tmp.ToString());
}
else if (list is AddWebSite)
{
AddWebSite tmp = (AddWebSite)list;
sb.Append(tmp.ToString());
}
else if (list is AddSerialCode)
{
AddSerialCode tmp = (AddSerialCode)list;
sb.Append(tmp.ToString());
}
}
File.WriteAllText(sfdSaveFile.FileName, sb.ToString());
}
I already have new instance of AddEntryWindow form here:
private void tsmiAddEntry_Click(object sender, EventArgs e)
{
if (storedAuth == null)
{
DialogResult result = MessageBox.Show
("You must log in before you add an entry."
+ Environment.NewLine + "You want to authenticate?",
"Information", MessageBoxButtons.YesNo,
MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
AuthenticationWindow authWindow =
new AuthenticationWindow();
authWindow.ShowDialog();
storedAuth = authWindow.Result;
AddEntryWindow addWindow = new AddEntryWindow
(this, storedAuth.UserName, storedAuth.Password);
addWindow.ShowDialog();
}
else
{
}
}
else
{
AddEntryWindow addWindow = new AddEntryWindow
(this, storedAuth.UserName, storedAuth.Password);
addWindow.ShowDialog();
}
}
Regards.
An idea can be creating third singletone class that only can have one instance of
array list and shares it so every class in your app can use it
public class ShareArray
{
private System.Collections.ArrayList arrayList;
#region Property
public System.Collections.ArrayList ArrayList { get{return arrayList;}}
#endregion
#region Imp. signletone
private static ShareArray instance;
public static ShareArray Instance
{
get
{
if (instance == null)
{
instance = new ShareArray();
}
return instance;
}
}
private ShareArray()
{
arrayList = new System.Collections.ArrayList();
}
#endregion
}
and use this class every where you want in this way
ShareArray.Instance.ArrayList.Add(value);
or
var myarr = ShareArray.Instance.ArrayList;
One solution is array list define as Static. but this isn't Good Choose.
Describe better your program design! Is form 1 open form 2?

How to return a value from a Form in C#?

I have a main form (let's call it frmHireQuote) that is a child of a main MDI form (frmMainMDI), that shows another form (frmImportContact) via ShowDialog() when a button is clicked.
When the user clicks the 'OK' on frmImportContact, I want to pass a few string variables back to some text boxes on frmHireQuote.
Note that there could be multiple instances of frmHireQuote, it's obviously important that I get back to the instance that called this instance of frmImportContact.
What's the best method of doing this?
Create some public Properties on your sub-form like so
public string ReturnValue1 {get;set;}
public string ReturnValue2 {get;set;}
then set this inside your sub-form ok button click handler
private void btnOk_Click(object sender,EventArgs e)
{
this.ReturnValue1 = "Something";
this.ReturnValue2 = DateTime.Now.ToString(); //example
this.DialogResult = DialogResult.OK;
this.Close();
}
Then in your frmHireQuote form, when you open the sub-form
using (var form = new frmImportContact())
{
var result = form.ShowDialog();
if (result == DialogResult.OK)
{
string val = form.ReturnValue1; //values preserved after close
string dateString = form.ReturnValue2;
//Do something here with these values
//for example
this.txtSomething.Text = val;
}
}
Additionaly if you wish to cancel out of the sub-form you can just add a button to the form and set its DialogResult to Cancel and you can also set the CancelButton property of the form to said button - this will enable the escape key to cancel out of the form.
I normally create a static method on form/dialog, that I can call. This returns the success (OK-button) or failure, along with the values that needs to be filled in.
public class ResultFromFrmMain {
public DialogResult Result { get; set; }
public string Field1 { get; set; }
}
And on the form:
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
To call your form;
public void MyEventToCallForm() {
var result = frmMain.Execute();
if (result.Result == DialogResult.OK) {
myTextBox.Text = result.Field1; // or something like that
}
}
Found another small problem with this code... or at least it was problematic when I tried to implement it.
The buttons in frmMain do not return a compatible value, using VS2010 I added the following and everything started working fine.
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
f.buttonOK.DialogResult = DialogResult.OK;
f.buttonCancel.DialogResult = DialogResult.Cancel;
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
After adding the two button values, the dialog worked great!
Thanks for the example, it really helped.
delegates are the best option for sending data from one form to another.
public partial class frmImportContact : Form
{
public delegate void callback_data(string someData);
public event callback_data getData_CallBack;
private void button_Click(object sender, EventArgs e)
{
string myData = "Top Secret Data To Share";
getData_CallBack(myData);
}
}
public partial class frmHireQuote : Form
{
private void Button_Click(object sender, EventArgs e)
{
frmImportContact obj = new frmImportContact();
obj.getData_CallBack += getData;
}
private void getData(string someData)
{
MessageBox.Show("someData");
}
}
I just put into constructor something by reference, so the subform can change its value and main form can get new or modified object from subform.
If you want to pass data to form2 from form1 without passing like new form(sting "data");
Do like that
in form 1
using (Form2 form2= new Form2())
{
form2.ReturnValue1 = "lalala";
form2.ShowDialog();
}
in form 2 add
public string ReturnValue1 { get; set; }
private void form2_Load(object sender, EventArgs e)
{
MessageBox.Show(ReturnValue1);
}
Also you can use value in form1 like this if you want to swap something in form1
just in form1
textbox.Text =form2.ReturnValue1
I use MDI quite a lot, I like it much more (where it can be used) than multiple floating forms.
But to get the best from it you need to get to grips with your own events. It makes life so much easier for you.
A skeletal example.
Have your own interupt types,
//Clock, Stock and Accoubts represent the actual forms in
//the MDI application. When I have multiple copies of a form
//I also give them an ID, at the time they are created, then
//include that ID in the Args class.
public enum InteruptSource
{
IS_CLOCK = 0, IS_STOCKS, IS_ACCOUNTS
}
//This particular event type is time based,
//but you can add others to it, such as document
//based.
public enum EVInterupts
{
CI_NEWDAY = 0, CI_NEWMONTH, CI_NEWYEAR, CI_PAYDAY, CI_STOCKPAYOUT,
CI_STOCKIN, DO_NEWEMAIL, DO_SAVETOARCHIVE
}
Then your own Args type
public class ControlArgs
{
//MDI form source
public InteruptSource source { get; set; }
//Interrupt type
public EVInterupts clockInt { get; set; }
//in this case only a date is needed
//but normally I include optional data (as if a C UNION type)
//the form that responds to the event decides if
//the data is for it.
public DateTime date { get; set; }
//CI_STOCKIN
public StockClass inStock { get; set; }
}
Then use the delegate within your namespace, but outside of a class
namespace MyApplication
{
public delegate void StoreHandler(object sender, ControlArgs e);
public partial class Form1 : Form
{
//your main form
}
Now either manually or using the GUI, have the MDIparent respond to the events of the child forms.
But with your owr Args, you can reduce this to a single function. and you can have provision to interupt the interupts, good for debugging, but can be usefull in other ways too.
Just have al of your mdiparent event codes point to the one function,
calendar.Friday += new StoreHandler(MyEvents);
calendar.Saturday += new StoreHandler(MyEvents);
calendar.Sunday += new StoreHandler(MyEvents);
calendar.PayDay += new StoreHandler(MyEvents);
calendar.NewYear += new StoreHandler(MyEvents);
A simple switch mechanism is usually enough to pass events on to appropriate forms.
First you have to define attribute in form2(child) you will update this attribute in form2 and also from form1(parent) :
public string Response { get; set; }
private void OkButton_Click(object sender, EventArgs e)
{
Response = "ok";
}
private void CancelButton_Click(object sender, EventArgs e)
{
Response = "Cancel";
}
Calling of form2(child) from form1(parent):
using (Form2 formObject= new Form2() )
{
formObject.ShowDialog();
string result = formObject.Response;
//to update response of form2 after saving in result
formObject.Response="";
// do what ever with result...
MessageBox.Show("Response from form2: "+result);
}
I raise an event in the the form setting the value and subscribe to that event in the form(s) that need to deal with the value change.

Categories

Resources