Form.Parent and StartPosition.CenterParent - c#

I need to show a form exactly in front of another form, this lead me to the following question.
How come a form can have a start position as CenterParent while having the field this.Parent equals to null?
It must know the parent in order to position itself correctly, which it does, but the Parent field is not set. This is odd. Am I missing something?
Form2 f = new Form2();
f.ShowDialog();
Thats all I do on the child form. The parent is set to default windows position. No matter where I move the parent form, the child is shown in the center of the parent.

The information about the owner is passed to the created dialog via the API call (you can see that in Reflector within the ShowDialog(IWin32Window owner) method):
UnsafeNativeMethods.SetWindowLong(new HandleRef(this, base.Handle), -8, new HandleRef(owner, handle));
When there is no owner specified in ShowDialog call the owner variable is calcualated via the GetActiveWindow API call:
IntPtr activeWindow = UnsafeNativeMethods.GetActiveWindow();
IntPtr handle = (owner == null) ? activeWindow : Control.GetSafeHandle(owner);
To get access to the Owner f dialog form you can use the GetWindowLong API call:
IntPtr ownerHandle = NativeMethods.GetWindowLong(nonModalForm.Handle, -8);

The difference between a parent and an owner of a window is a bit muddled in Winforms. In this context, CenterParent really means CenterOwner. Not a Windows capability, it is implemented in Winforms, done by the base Form.OnLoad() method. A dialog should always have a owner, it goes out hunting for one when you don't specify one in the ShowDialog() call. Which uses GetActiveWindow() as the best guess.
To see this for yourself, overload OnLoad in the dialog form, like this:
public partial class Form2 : Form {
public Form2() {
InitializeComponent();
this.StartPosition = FormStartPosition.CenterParent;
}
protected override void OnLoad(EventArgs e) {
//base.OnLoad(e);
}
}
And note that it no longer centers on the owner.
You can see the relevant code in the Reference Source, Form.cs class, OnLoad method:
if (GetState(STATE_MODAL)) {
FormStartPosition startPos = (FormStartPosition)formState[FormStateStartPos];
if (startPos == FormStartPosition.CenterParent) {
CenterToParent();
}
else if (startPos == FormStartPosition.CenterScreen) {
CenterToScreen();
}
}

I believe this information is stored internally by .NET Framework for some reason. Anyway, if you want to have access to the parent form you can set it to Owner property of the child form:
Form form = new Form();
form.Owner = this;
form.Show();
Then in the child form you can access the parent form using the Owner property. When a form is owned by another form, it is minimized and closed with the owner form. Owned forms are also never displayed behind their owner form, which is exaclty what you need if I understood you right.

This code will open the dialog in the middle of the parent TAG.
ObjectDistrubution od = new ObjectDistrubution();
od.StartPosition = FormStartPosition.CenterParent;
od.ShowDialog();

Related

Disallowing interaction with background form

On my application's first run, two forms open. The topmost form needs to take priority, and disallow any interaction with the form in the background. I have tried ShowDialog() as referenced here, however this hides the form in the background which I do not wish to do. Is there a method of accomplishing this?
public Form1()
{
InitializeComponent();
if (!fileexists(#"c:\Management Tools\Absence Tracker\bin\data\tbase.skf"))
{ firstrunactions(); }
}
void firstrunactions()
{
//open the get-started form and invite user to populate serialisable objects
firstrun frwindow = new firstrun();
frwindow.ShowDialog();
}
When you are using .ShowDialog() the execution of the containing method is paused until you close the newly opened window. So make sure to do everthing else before you call .ShowDialog(). Otherwise your program gets stuck in this method. If you are calling .ShowDialog() before the background window is shown will cause problems.
But using .ShowDialog() here is totally correct and has the right functionality.
Example how not to do it (causes the same behavior like in your problem):
public Form1()
{
InitializeComponent();
//this is the wrong place for showing a child window because it "hides" its parent
Form frwindow = new Form();
frwindow.ShowDialog(this);
}
The magical place where it works:
private void Form1_Shown(object sender, EventArgs e)
{
Form frwindow = new Form();
frwindow.ShowDialog(this);
}
Edit: In your case it is enough moving if(!fileexistst...) into the Form1_Shown()-event.
Try with frwindow.ShowDialog(this);
Or instead "this" pass the other form as parameter.
Also move this part if (!fileexists(#"c:\Management Tools\Absence Tracker\bin\data\tbase.skf"))
{ firstrunactions(); }
}
in OnLoad override.

Open second form, right next first form

I'am using winforms.
I created an application which is nearly finished. Consider the following: I have two forms, the first form starts at application startup, the second form needs to be opened right next to the first form.
Example:
How can I access the location of the first form at the second form? Should I send "this" to the constructor of the second form?
EDIT
following code helped me out:
private void changelogToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_changelog.IsDisposed)
{
_changelog = new Changelog();
}
_changelog.Location = new Point((Left + Width), Top);
_changelog.Show();
}
A basic rule to keep in mind when designing one's constructor: Never give any unnecessary information to the constructor.
So, what you need here is not the other window, but rather it's position.
Even better, you need the position where your new window should be located at.
This means that you shouldn't let the second form know about the first form, instead it's constructor should take either:
One parameter Point location
Two parameters int x, int y
Depending on your preferance.
You could (should) of course have both constructors, so you can decide whether to give Point location or int x, int y.
This all being said, forget what you read.
Better than using a constructor at all, I would just set the property manually when creating the second form:
SecondForm form = new SecondForm()
{
Location = new Point(this.Right, this.Top)
};
Which is just an other way of saying:
SecondForm form = new SecondForm();
form.Location = new Point(this.Right, this.Top);
Why do not position the new form when you open it?
Form2 f = Form2();
f.Location = new Point(this.Left + this.Width, this.Top);
f.Show(); // Or ShowDialog()
Of course, this requires that the second form property StartPosition is set to FormStartPosition.Manual

Pass in parent form to ShowDialog that is called from a class instance

I have a form.
In that form I create an instance of a class on a new thread because it runs some long running logic. The form also gives the user the ability to cancel this logic/thread.
That class opens a new form if input is required.
The new form sometimes appears behind the other form.
I set a property on the class:
public Form ParentForm{get;set;}
I can now do:
MyForm form = new MyForm();
form.ShowDialog(ParentForm);
However I get a cross thread exception when calling ShowDialog(ParentForm).
I know I can use InvokeRequired somehow but not sure how on a property.
Thanks
UPDATE: Have tried doing this but still get exception:
MyForm form = new MyForm();
form.ShowDialog(GetParentForm());
private Form GetParentForm()
{
//You have to Invoke() so you can wait for the function to return and obtain its return value.
if (ParentForm.InvokeRequired)
{
return (Form)ParentForm.Invoke(new Func<Form>(() => GetParentForm()));
}
else
{
return ParentForm;
}
}
Your updated method (GetParentForm) won't work because you're wrapping the task of getting the reference to ParentForm in an InvokeRequired block. You could try wrapping the ShowDialog call in such a block instead, but I think you would still get the cross-threading error.
Your simplest fix would be to move the code that creates and shows the second form out of your class and into ParentForm. So instead of this:
MyForm form = new MyForm();
form.ShowDialog(ParentForm);
you would do this:
ParentForm.showMyNewForm();
and in ParentForm you would have this:
public void showMyNewForm()
{
MyForm form = new MyForm();
form.ShowDialog(this);
}
If MyForm needs to have a reference to the class on the other thread, you would just add a parameter to showMyNewForm() so that the reference to it can be passed in.
What you're trying to do here (creating and showing related, connected forms that are created on different threads) is really going against the grain of how forms are meant to be used in .NET.
you can add async method to a form.
Let's say like this:
public class MyForm : Form
{
public void ShowModalAsync()
{
this.Invoke(new Action(()=> {
ShowDilaog(..);
}));
}
}
and use this, like:
MyForm form = new MyForm();
form.ShowModalAsync(...);
Should work for you.
By the way, if your problem is only the fact that the window appears on bihind of others, try to make use of Form.TopMost property setting it to true. Having in mind that it, yes, will bring it infront of other forms, but not necessary infront of other topmost forms.

Easy Way To Close Form

This question might sound simple but I can't find the answer i'm looking for.
I have a Login_Form and a Main_Form.
Once I log into the Main_Form I just make it visible and I activate it because I don't want my Login_Form to close.
My question is, is there a way to close both forms at the same time from the Main_Form?
The this.Close(); will only close the current form.
if (access)
{
Main_Form mainForm = new Main_Form();
mainForm.Visible = true;
mainForm.Activate();
}
This is where I Instantiate the main_form how can I pass a reference to the login_form?
Thanks in advance and remember the Login_Form must remain open until I close it from the Main_Form!
Feedback
After looking at which of your answers I would use I found out about the Application.Close() Method Which closes all the forms. Should have taught about this before posting here thanks everyone.
If your Main form has a reference to the Login form you can call the close method on it. How you do that is up to you.
You could pass the reference in to the Main from FROM the login form when you show it.
You could keep a global reference to the Login Form. (think singleton)
any other way you can think of
For example:
public class MainForm : Form
{
Form loginform;
public MainForm(Form loginForm)
{
this.loginForm = loginForm;
}
public void CloseForms()
{
loginForm.Close();
this.Close();
}
}
In your Active() method, just pass the login form in. Alternatively you can use the code above, and just pass this into the constructor instead of the Active() method.
public class MainForm : Form
{
Form loginform;
public Active(Form loginForm)
{
this.loginForm = loginForm;
}
public void CloseForms()
{
loginForm.Close();
this.Close();
}
}
//from the LOGIN form
if (access)
{
Main_Form mainForm = new Main_Form();
mainForm.Visible = true;
mainForm.Activate(this); //this is a reference to the current form. LOGIN in this case
}
If you have a reference to the login form then call Close on that form.
Login_Form.Close()
You will have modify the constructor of Main_Form to accept login form instance as a parameter
You can also use the AddOwnedForm Method.
From above link:
When a form is owned by another form, it is closed or hidden with the
owner form. For example, consider a form named Form2 that is owned by
a form named Form1. If Form1 is closed or minimized, Form2 is also
closed or hidden. Owned forms are also never displayed behind their
owner form. You can use owned forms for windows such as find and
replace windows, which should not be displayed behind the owner form
when the owner form is selected.
Code:
Form loginForm = new Form();
this.AddOwnedForm(loginForm);
loginForm.Show();
Edit:
Form mainForm = new Form();
mainForm.Visible = true;
mainForm.AddOwnedForm(this);
mainForm.Activate();
now if you close the mainForm it will close both forms.
You could just use Application.Exit(); not Application.Close();

Changing the property of a control on another form

Basically, I have a settings window, and when you click "OK", it's suppose to apply settings to the main form (eg, set font of a control, etc), and then close.
frmmain frm = new frmmain();
frm.OLVAltBackColor = Color.Aquamarine ;
I tried that, but it only applies the settings to that instance, and you can see it if you do frm.Show();
I'm trying to make it so the already opened form has it's control's properties changed.
What you are trying to do is not working because you are creating a NEW instance of your main form and updating that rather than the first instance. It is possible to update the main form by keeping a reference to it in your settings form... but...
...it sounds like you are approaching this from the wrong direction.
Don't make the settings form dependent on the main form. Instead create the settings form from the main dialog.
class SettingsForm : Form
{
// You need to ensure that this color is updated before the form exits
// either make it return the value straight from a control or set it
// as the control is updated
public Color OLVAltBackColor
{
get;
private set;
}
}
In your main form
(I'm assuming some kind of button or menu click)
private void ShowSettingsClicked(object sender, EventArgs args)
{
using (SettingsForm settings = new SettingsForm())
{
// Using 'this' in the ShowDialog parents the settings dialog to the main form
if (settings.ShowDialog(this) == DialogResult.OK)
{
// update settings in the main form
this.OLVAltBackColor = settings.OLVAltBackColor;
}
}
}
Apply the property change to the form that already exists and is already shown instead of creating a new form and changing that one.
In this code you're creating a new instance of the frmmain. Any changes you make to that new object will happen in the new object, not the one you actually want to change.:
frmmain frm = new frmmain(); //Creating a new object isn't the way.
frm.OLVAltBackColor = Color.Aquamarine ;
What you're looking for is a way to call on the already existant frmmain class and change the property of that.
Edit, for example:
using System;
class Statmethod
{
//A class method declared
static void show()
{
int x = 100;
int y = 200;
Console.WriteLine(x);
Console.WriteLine(y);
}
public static void Main()
{
// Class method called without creating an object of the class
Statmethod.show();
}
}

Categories