set variable of parent form from child form in mdi - c#

How would one switch a public bool to true from a child form in a mdi type program?
I have a child form called logon that if everything checks out i want to set a "authenticated" bool to true in the form1 (main) form

The proper, true OO way of doing things would be to expose an event on your child form that the parent can attach to. You're violating your separation of concerns if you have the child form make assumptions about its MdiParent.
For example, a very simple method of doing what you describe would be to have this on your child form:
public event EventHandler Authenticated;
The when the parent opens it...
YourForm newForm = new YourForm();
newForm.Authenticated += new EventHandler(newForm_Authenticated);
newForm.MdiParent = this;
// and so on
You could also go slightly more sophisticated (and I do mean slightly) by adding an Authenticated boolean property to your child form, and rename the event to AuthenticatedChanged. You could then use the same event handler to inspect the value of the property to determine if the user has authenticated.
In either scenario, you simply raise your event from the child form when you want the parent to update.

You could make a globally accessible variable that holds the main form, then use that variable within the child to call methods on the main form.
Or, you could cast the appropriate Parent or Owner property of the child window to the proper type of the main form, and work from there.

Since I noticed you are using a "logon" form you could try the following: set the logon form's DialogResult property according to username/password testing success. I am using username/pass just as an example.
On the logon form do something like:
if(isMatch(username, password)){
this.DialogResult=DialogResult.OK;
this.Close();
}
else MessageBox.Show("Logon error - try again!");
// or anything else you would like to do in case of an error
And then on the parent form:
LogonForm f = new LogonForm();
if(f.ShowDialog() == DialogResult.OK){
// continue
}
else {
// abort
}

Related

Owner of Form which is not inside Application.Run

I'm trying to show a Form before an application's start and get its DialogResult, so I'm just creating it and using ShowDialog (because Application.Run's return value is void).
What I'm worried about is that it might get 'hijacked' by mistake by some other Form that might be shown at the time. Not by this application, obviously. See What is the meaning of Form.Show(null)? that it's not advisable to use the parameterless overload of ShowDialog.
I have tested and seen that the Form's Owner property was null. But will it always be so? Or should I create a Form and use that as the Owner without showing it? That seems a strange solution but logically it should avoid any problem. Or will that introduce new ones?
Not by this application, obviously
This is already taken care of by Windows, it enforces a strong separation between processes and windows owned by threads. A typical choice for the owner of a dialog for example is the window returned by GetActiveWindow(). The active window is a property of a thread. Which explains for example why a MessageBox.Show() call made from a worker thread is never modal to the rest of the windows.
Making a window modal against the windows of another process is technically possible but requires lots of effort. The app would have to call AttachThreadInput(), a very unsubtle winapi function that nobody ever calls by accident. Also a great source of deadlock.
Unless you are programming in a boat near the Somali coast, there is no good reason to fear your window getting hijacked.
[STAThread]
static void Main()
{
Form1 form1 = new Form1();
//here I suppose the form you want to show
Form1 form2 = new Form2();
form2.ShowDialog(form1);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(form1);
}
Basically you use ShowDialog() when there are no parents for this window. Usually this happens for the main window. If you are opening one window after another, while closing previous, then there will be multiple ShowDialog()s.
If you are showing dialog (which is also a window), then you can specify it's parent to achieve a certain behavior. To example, when alt-tabbing to that window, it's dialog will be shown in front. Think about this as making child-parent relations.
I don't know about the case, when multiple forms are the claiming same parent. But it sounds like a clear mistake, to example:
public Form1 FormMain = new Form1();
...
// show main form
FormMain.ShowDialog();
...
// somewhere in the main form - show dialog
Form2 form2 = new Form2();
form2.ShowDialog(FormMain);
...
// somewhere in form2 - show dialog
Form3 form3 = new Form3();
form3.ShowDialog(FormMain); // wrong, should be form2!
This is not tested because I couldn't recreate a case where the parent is ever anything but null (i.e. a reference to a third-party un-managed parent), but maybe you could do something like this on your form to set the parent to null if it changes:
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
this.ParentChanged += MyParentChanged;
}
public void MyParentChanged(Object sender, EventArgs e)
{
this.Parent = null;
}
}

Cannot access a disposed object error

I am getting the error message Cannot access a disposed object.
Object name: 'ApplicationProperties'. when I tryo to re-open a form after closing it. I have noticed this is from exiting forms, and exiting is "Disposing" of them, so I have put the following code in all of my accept buttons, and cancel buttons (any button that closes a form).
this.Hide();
this.Parent = null;
This code is just hiding the form. Not closing the form.
So what my problem is, is that when I click the 'x' button on the form, then try to re-open the form I still get the error message. I have tried a couple differnet ways to modify the exiting funciton of the form such as:
private void ApplicationProperties_FormClosing(object sender, FormClosingEventArgs e)
{
//Hiding the window, because closing it makes the window unaccessible.
this.Hide();
this.Parent = null;
}
But this has brought me no luck. I was wondering if anyone knows how to solve this problem. Here is the code that is working for me inside my cancel and accept buttons. It is the same for all of my buttons that close forms.
private void OptionsCancelbtn_Click(object sender, EventArgs e)
{
//Hiding the window, because closing it makes the window unaccessible.
this.Hide();
this.Parent = null;
}
I have declared the instance at the top of my class on form1, and have a button inside form1 that opens form2.
public partial class MainBox : Form
{
//Making a name for the ApplicationProperties form. It can be opened when called.
ApplicationProperties ApplicationPropertiesWindow = new ApplicationProperties();
private void ApplicationPropertiesbtn_Click(object sender, EventArgs e)
{
//Show the properties window.
ApplicationPropertiesWindow.Show();
}//End ApplicationProperties button.
}
After I close the program with the 'x' button on the second form I cannot access form2 again because of the error message firing at ApplicationPropertiesWindow.Show();
Inside form2 I have the following code:
public partial class ApplicationProperties : Form
{
//Creates and sets the instance MainBoxWindow.
public MainBox MainBoxWindow { get; set; }
Try this in your FormClosing event:
private void ApplicationProperties_FormClosing(object sender, FormClosingEventArgs e)
{
//Hiding the window, because closing it makes the window unaccessible.
this.Hide();
this.Parent = null;
e.Cancel = true; //hides the form, cancels closing event
}
cause right now it is still closing the form, thus still causing the error.
After I close the program with the 'x' button on the second form I
cannot access form2 again because of the error message firing at
ApplicationPropertiesWindow.Show();
When a form is closed (Form.Close), the form itself and all of its associated resources are freed. There are only two exceptions to this automatic disposal, as noted in the documentation:
The form is part of a MDI application and not visible.
The form was displayed as a modal dialog using ShowDialog (as opposed to Show). It is designed this way so that you can access properties of the dialog (e.g. to retrieve user input) after the user has closed it.
In both of these special cases, you are responsible for manually calling the Dispose method of the form. The second case is by far the most common (no one really uses the MDI paradigm anymore), and is handled easily with a using statement:
using (MyDialogBox dlg = new MyDialogBox())
{
DialogResult result = dlg.ShowModal(this);
if (result == DialogResult.Yes)
{
// access members of "dlg", and
// do whatever the user asked
}
} // the Dispose method is automatically called here
In your case, as is typical, the call to the Close method is closing and destroying the form. You already know that you cannot access a disposed object (because it no longer exists!), so that's why you're getting the exception when you try to show it. In order to show the form again after it has been closed, you need to create a new instance of the form class:
MyForm frm = new MyForm();
frm.Show();
// ...
frm.Close();
This is really the best method. A new instance of the form will be identical to the one you're closing because it was created from the same class. You would do best to start thinking in object-oriented terms and avoid singleton-based designs whenever possible. Each form that shows up on the screen is a new, independent object. Once that object is closed and destroyed, you cannot use it any longer. If you want to use it again, you need to create a new object and display it.
The Hide method is more of a hack, useful only when you want to temporarily hide the current instance of a form while still retaining its state (e.g., the values of its member properties, its control states, etc.). This works only with singleton objects that will never be destroyed, and must be carefully designed and maintained. It also means that this form object consumes resources all the time, whether it is being used or not, which is wasteful.
If you must do this, you'll need to track down what is causing your form instance to be disposed. Without seeing all of your code, it's hard for me to do anything but guess at where the problem might be. It is likely related to your use of the AcceptButton and/or CancelButton properties. Either way, the best and cleanest solution is to override the OnFormClosing method and prevent your form from ever being closed. You'll hide it instead:
public class MyForm : Form
{
protected virtual void OnFormClosing(FormClosingEventArgs e)
{
// Prevent the form from closing.
e.Cancel = true;
// Hide it instead.
this.Hide();
}
// ...other code in your form class
}
The advantage of this is that you only have to have the code in one place, local to the responsible class, rather than exposed to external code and scattered throughout your application. And of course, it also prevents the form from ever being closed by framework code outside of your control.
I have no idea why you're setting the Parent property to null. A top-level window (which is what all forms are) can never have a parent. Only child windows (e.g. controls) have parents. It can have an owner, but it will not necessarily. It depends on whether you pass an owner window as an argument when calling the Show method.
First remove this.Parent=null bcose this is not required when you hiding the Form
Now when you hiding the Form and if you still want to access this Form store form in static var . Bcous when object is no longer in use garbage collector dispose it and it will no longer available .
The problem, at least perhaps part of it, looks like a bit of class interdependence.
When you initialize an ApplicationProperties instance, you're creating a reference to a MainBox object, but in the definition of the MainBox class, you're creating a new ApplicationsProperties object, which references a MainBox.... I'm even confusing myself. Is MainBox your parent form, the one that loads when you start the application?
I wouldn't be surprised if some stuff in MainBox is getting destroyed inadvertently if you set a reference to it in the ApplicationProperites class... which is disposing when you click "X". Again, this is an educated guess; I'm not sure if that's the reason but even if the code isn't wrong per se, it looks quirky to me.
If you want your ApplicationProperties window to last forever and to simplify things, just initialize a static instance of it at the beginning of whatever form is going to persist the duration of your application, and show/hide it similar to what you were attempting originally. If you want to be efficient and fundamental, construct and dispose ApplicationProperties whenever you need to let the user modify it, but kill the circular dependency.

Set form as Parent throw exception "Top-level control cannot be added to a control"

I want to access variables of a form from another form. On clicking a button inside my Main form, I want to set my Main form as Parent, then bring up another form (child form) wherein I will access variables of the Main form. My click handler is as follow:
private void btnSystem_Click(object sender, EventArgs e)
{
Form_EnterPassword EP = new Form_EnterPassword();
EP.Parent = this; //error: Top-level control cannot be added to a control
EP.ShowDialog();
}
It compiles fine without any error. However, when I run the Main form and click on the System button, it throws me an exception. I do something similar in another code (not mine) with the same button click, and encounter no error (just with setting Main form as Parent).
What am I doing wrong? Is there something in my Main code that cause this?
Best way would be to use EP.ShowDialog(this) and later use Owner property.
You need the EP.TopLevel property to be set to false. It will let you to set a parent to it.
Further reading.
In case you only want to access variables and controls of another form, then maybe you can reach it in other ways, not trough a Parent relationship.
OK,
apparently the way to do it is to call
Form_Child.ShowDialog(this)
and then I can call
FromParent_aVariable = ((Form_Parent)this.Owner).aVariable;
or if I define aVariable in the namespace Properties then
FromParent_aVariable = NameSpace.Properties.Settings.Default.aVariable;
there are two ways.
Form_EnterPassword EP = new Form_EnterPassword();
EP.MdiParent = this;
EP.Show();
try this way, it helps for me. you need to set principalform as isMdicontainer = true at the form properties
I had a similar situation recently.
I was attempting something similar but by controlling the Child Forms from a different class.
Note(s):
You're trying to set the Child Form(s) "TopMost" to something that does not allow it.
In this case the "MdiContainer".
To accomplish this:
• Disable MainForm "isMdiContainer" property (its use is kind of obsolete anyway).
• Set the Form(s) TopMost properties to true.
• You should now be able to accomplish your feature.
**Code Example:**
/* On your Main Form Class */
private void btnSystem_Click(object sender, EventArgs e)
{
// Instantiate the Form_EnterPassword by passing the MainForm
Form_EnterPassword EP = new Form_EnterPassword(this);
EP.Show(); // No longer as modal Form to display in front.
}
/* Under your EnterPassword Form Class */
// Do not create a new Instance of MyMainForm.
// You want to use the same thread as your MainForm
private MyMainForm mainForm;
/* Constructor */
public Form_EnterPassword(MyMainForm form)
{
mainForm = form;
this.Owner = mainForm; // "this" refers to the: EnterPassword Form.
}
Remarks:
The only additional thing that you (may) have to do, (to achieve perfection) is to check the MainForm > WindowState; and create a code block to minimize or bring the Forms to their specific state.
i.e:
if (WindowState == FormWindowState.Minimized)
{ /* Code to Minimize all the Child Forms. */ }
else { /* Code to bring all Forms to their "Normal" State */ }
Writing this way, made the dialog display on the center of the parent form.
Form_Child.StartPosition = FormStartPosition.CenterParent;
Form_Child.ShowDialog(this);

Windows Form Launching Another Form

I am quite new to Visual Studio (Express) and C#. I have made a windows form that accepts some user input then displays that input in a Message Box (that automatically comes with an "OK" button that closes the Message Box when clicked).
Instead I would like the user input collected by the first form to be displayed in a new form that displays a message (label), shows the input, and offers a choice of two buttons: one to accept and one to go back and change the input.
I have NO idea how to do this and any advice is appreciated.
Sounds like you need a confirm Message or something similar.
The MessageBox class offers this functionality.
DialogResult btn = MessageBox.Show("your message",
"your title",
MessageBoxButtons.OKCancel,
MessageBoxIcons.Question);
if(btn == DialogResult.Cancel)
// User canceled, return to the string editor
else
// User confirmed, do you work
If you prefer there is also an enum for MessageBoxButtons.YesNo with corresponding DialogResult.Yes and DialogResult.No
See here for a reference on MessageBoxButtons
See here for a reference on MessageBoxIcons
What you need is to somehow pass the information from the first form to the second. This can be done by setting properties on the child form, either through individual properties (strings, ints, etc.) or by a complete data structure (object). The input form collects the information, creates the sub child form, sets properties on it, then displays the form. Many other ways to do this, but start with the simple and then build up to the complex.
If you need to pass some data to your second form, create property/properties on that form, or provide data via constructor parameters. Also assign DialogResult property for two buttons on second form. Set DialogResult.OK to button which will accept input. Verify value returned by second form, when you show it as dialog and do appropriate actions:
using(SecondForm secondForm = new SecondForm()
{
secondForm.Data = yourData;
if (secondForm.ShowDialog() != DialogResult.OK)
{
// go back and change input
return
}
// accept input
}
So your Form2 needs to have some value, provided by whatever creates it, for it to exist. There should never be an instance of Form2 without that information. That tells you that it should be in that form's constructor (as opposed to a property on that form).
This means that in Form1 you will have something like this:
string someData; //populate based off of user input
Form2 childForm = new Form2(someData);
//then hide current form and show child form
In Form2 you probably already have a constructor, you just need to modify it to something like:
public Form2(string someData) //TODO give better parameter name
{
someLabel.Text = someData;
}
Next we need to deal with the child form going back to the parent form. I feel the preferable way to deal with this is using events. The form has a FormClosing event that you can attach to; this will allow your parent form to run some code when the child form is closed.
string someData; //populate based off of user input
Form2 childForm = new Form2(someData);
childForm.FormClosing += (sendingForm, args) =>
{
this.Show();
bool result = childForm.DidUserAccept;
}
Here I used a property on the child form DidUserAccept, for whether the user accepted or declined the value. We'll need to define that in Form2:
public bool DidUserAccept {get; private set;}
In the button click handlers for accept/cancel you can set the result accordingly and then close the form (closing will trigger the closed event and runt he relevant code in Form1.

Set the Parent of a Form

I have a Windows form from which I would like to open a status form that says "Saving..."
and then disapears when the saving is complete. I would like to center this small status form in the middle of the calling form. I've tried setting the "StartPosition" propery to "CenterParent", but it doest work. I create the status form from the other form like so:
SavingForm saving = new SavingForm();
savingForm.Show();
Thread.Sleep(500); //Someone said this is bad practice ... why?
savingForm.Close();
Wouldn't the calling form be the "Parent"?
When I set a watch for saving it says it has no parent.
I tried:
SavingForm saving = new SavingForm();
saving.Parent = this;
savingForm.Show();
Thread.Sleep(500);
savingForm.Close();
and it throws an exception "Top-level control cannot be added to a control."
How do I center this status window in the calling window?
Thanks in advance
i would do something like this:
SavingForm saving = new SavingForm();
savingForm.ShowDialog(this);
In SavingForm i would start a timer in the load handler that runs for 500 milliseconds and then closes the form when done. Cleaner that way. ShowDialog will also lock your UI to only display the saving form and not allow the user to monkey with anything.
Use this:
saving.Show(this);
To set the Owner when you show the form.
Edit: The ShowDialog() method also has an overload that let's you specify the owner if that is the route you decide to go:
saving.ShowDialog(this);
If you pass the parent (this) to the Owner, like
SavingForm saving = new SavingForm() { Owner = this };
then you can access Owner's properties and methods in the child form (in this case SavingForm), provided that the Owner's properties Modifier is set to Internal or Public for each property you need to access (you can either edit the modifier directly in the source code, or via form's designer properties - there is a Modifier property for each control).
You can find a nice explanation of the differences between Owner, Parent and ParentForm here.
Note: Passing it like saving.Show(this); or saving.ShowDialog(this); did not help in my case.

Categories

Resources