38

In the following code, only the second method works for me (.NET 4.0). FormStartPosition.CenterParent does not center the child form over its parent. Why?

Source: this SO question

using System;
using System.Drawing;
using System.Windows.Forms;

class Program
{
  private static Form f1;

  public static void Main()
  {
    f1 = new Form() { Width = 640, Height = 480 };
    f1.MouseClick += f1_MouseClick; 
    Application.Run(f1);
  }

  static void f1_MouseClick(object sender, MouseEventArgs e)
  {
    Form f2 = new Form() { Width = 400, Height = 300 };
    switch (e.Button)
    {
      case MouseButtons.Left:
      {
        // 1st method
        f2.StartPosition = FormStartPosition.CenterParent;
        break;
      }
      case MouseButtons.Right:
      {
        // 2nd method
        f2.StartPosition = FormStartPosition.Manual;
        f2.Location = new Point(
          f1.Location.X + (f1.Width - f2.Width) / 2, 
          f1.Location.Y + (f1.Height - f2.Height) / 2
        );
        break;
      }
    }
    f2.Show(f1); 
  }
}
Community
  • 1
  • 1
kol
  • 27,881
  • 12
  • 83
  • 120

14 Answers14

43

This is because you are not telling f2 who its Parent is.

If this is an MDI application, then f2 should have its MdiParent set to f1.

Form f2 = new Form() { Width = 400, Height = 300 };
f2.StartPosition = FormStartPosition.CenterParent;
f2.MdiParent = f1;
f2.Show();

If this is not an MDI application, then you need to call the ShowDialog method using f1 as the parameter.

Form f2 = new Form() { Width = 400, Height = 300 };
f2.StartPosition = FormStartPosition.CenterParent;
f2.ShowDialog(f1);

Note that CenterParent does not work correctly with Show since there is no way to set the Parent, so if ShowDialog is not appropriate, the manual approach is the only viable one.

Vijay Patel
  • 17,094
  • 6
  • 31
  • 35
competent_tech
  • 44,465
  • 11
  • 90
  • 113
  • 1
    How could I do this? `f2.Parent = f1;` cannot be compiled. Please check out the SO question I referred above, there is code example where the Parent is not set explicitly, and FormStartPosition.CenterParent works. – kol Dec 19 '11 at 20:30
  • 1
    Nice try, but this is not an MDI application :) Just look at the code above: that is the whole program, and it's clearly not MDI; and I call Show with f1; and this is .NET 4. – kol Dec 19 '11 at 20:33
  • 2
    @kol: Sorry, sometimes people post pseudo-code or an altered version of their application, which is why I provided both MDI and non-MDI alternatives. – competent_tech Dec 19 '11 at 20:37
  • @kol: Did a little more research and updated the answer; basically, because show does not assign a parent and because you cannot manually assign a parent, unless you call ShowDialog, you have to use the manual positioning route. – competent_tech Dec 19 '11 at 20:45
  • @competent_tech there is an overload for Show to pass the owner! Why wouldnt it help? :( – nawfal Dec 19 '11 at 23:51
  • Because Microsoft didn't code it that way :). When that overload is called, it only sets Owner, not Parent, and Owner doesn't count for CenterParent. – competent_tech Dec 19 '11 at 23:55
  • @competent_tech even the ShowDialog gets only IWin32Window owner as the paramter, but somehow it gets its parent that way :) – nawfal Dec 19 '11 at 23:59
  • And the strange part is, even if you dont pass the owner in ShowDialog, the modal form always center parent if the property of child form is accordingly set, in .net 4 – nawfal Dec 20 '11 at 00:02
  • I had this issue and realised it was because I had accidentally removed " InitializeComponent();" from the sub-form's constructor. Once I added it back again, all was fine. – Nick Allan Apr 12 '19 at 15:23
40

If you set the owner of the child form like so:

Form2 f = new Form2();
f.Show(this);

You can then center it easily like this:

Form2_Load(object sender, EventArgs e)
{
    if (Owner != null)
        Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2,
            Owner.Location.Y + Owner.Height / 2 - Height / 2);
}
JYelton
  • 35,664
  • 27
  • 132
  • 191
  • 1
    Instead of calculating this manually you can just use [CenterToParent()](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.centertoparent) instead. – GrahamS Jan 27 '22 at 02:18
16

I'm using this code inside my main form, hope it helps:

var form = new MyForm();
form.Show();
if (form.StartPosition == FormStartPosition.CenterParent)
{
    var x = Location.X + (Width - form.Width) / 2;
    var y = Location.Y + (Height - form.Height) / 2;
    form.Location = new Point(Math.Max(x, 0), Math.Max(y, 0));
}
deerchao
  • 10,454
  • 9
  • 55
  • 60
8

I found setting the location manually is the only reliable option in some more complex cases when form is auto-sized and dynamically modified.

However rather than computing the coordinates manually, I'd suggest using existing method:

this.CenterToParent();
sherpa
  • 326
  • 2
  • 11
  • 2
    MSDN: "Do not call the CenterToParent method directly from your code. Instead, set the StartPosition property to CenterParent." http://msdn.microsoft.com/en-us/library/system.windows.forms.form.centertoparent.aspx – kol May 22 '13 at 19:02
  • 2
    @kol Good point. If setting the StartPosition is sufficient then for sure this is the way to go. But there are cases where it does not (e.g. the size changes dynamically). Then this is the right manual approach, I believe. – sherpa May 23 '13 at 09:32
  • 1
    Some additional details: CenterToParent is called from the OnLoad method automatically if the form is being displayed as a modal dialog (`ShowDialog`). When using `form.Show(owner)`, since it is not a modal, this method is not called automatically. In that case calling this method yourself from OnLoad method is the correct approach. – caesay Aug 10 '20 at 21:36
5

I had the same problem, I eventually went with this:

protected override void OnActivated(EventArgs e) {
    if(this.Modal == false && this.StartPosition == FormStartPosition.CenterParent) {
        if(!(this.Owner is Form)) {
            // Center to the last form opened before this one
            int numforms = Application.OpenForms.Count;
            this.Owner = Application.OpenForms[numforms - 2];
        }
        this.CenterToParent();
        Application.DoEvents();
    }
    base.OnActivated(e);
}

Used as:

MyForm form = new MyForm();
form.Show(this); // with or without

The main advantage is that it does what your colleagues expect it to do, without requiring any hack in the calling form.

primo
  • 1,392
  • 16
  • 27
4

I found a solution that will center modeless window position to parent's position, and the child window can be still covered by parent window. You just have to call

f2.Show(f1);

which will set f2 owner to f1, f2 will show over the f1 at it's center position.

Next you set

f2.Owner = null;

and there you go, f2 is a separate window, with correct startup position.

Szybki
  • 1,083
  • 14
  • 29
  • This doesn't work. `form.Show(owner)` does not center form on owner. – Dan Bechard Mar 17 '16 at 15:52
  • Did you set form.StartPosition = CenterParent before calling form.Show(owner)? – Szybki Mar 17 '16 at 23:44
  • Indeed, I did. I tried both via designer, and via code. With and without `Owner = null`, though I'm honestly not sure why that would have any effect. – Dan Bechard Mar 18 '16 at 13:31
  • Since this was an old issue, I don't remember what really worked for me, but apparently you're right, form.Show(owner) doesn't center the form to owner, but if you use this.CenterToParent();, as @sherpa suggested in his answer http://stackoverflow.com/a/16689861/2572796, after calling form.Show(owner) but before setting form.Owner to null, this should solve your problem. – Szybki Mar 19 '16 at 11:30
  • I ended up just creating a window manager class to handle this, since I wanted to focus already-open windows (rather than allow duplicate editor windows) as well as cascade window positions (like WindowsDefaultLocation, but centered on the parent form rather than an arbitrary screen location). – Dan Bechard Mar 21 '16 at 12:23
2

I realize this is an old question, but I was recently having the same problem and for reasons I won't get in to, I did not want to use the form.ShowDialog() method and my application was not an MDI application, therefore the CenterParent method was not having any effect. This is how I solved the problem, by computing the coordinates for the form that I wanted centered and triggering the new location in the main form's LocationChanged event. Hopefully this will help someone else having this problem.

In the example below, the parent form is called MainForm and the form I want centered in MainForm is called pleaseWaitForm.

private void MainForm_LocationChanged(object sender, EventArgs e)
    {
        Point mainFormCoords = this.Location;
        int mainFormWidth = this.Size.Width;
        int mainFormHeight = this.Size.Height;
        Point mainFormCenter = new Point();
        mainFormCenter.X = mainFormCoords.X + (mainFormWidth / 2);
        mainFormCenter.Y = mainFormCoords.Y + (mainFormHeight / 2);
        Point waitFormLocation = new Point();
        waitFormLocation.X = mainFormCenter.X - (pleaseWaitForm.Width / 2);
        waitFormLocation.Y = mainFormCenter.Y - (pleaseWaitForm.Height / 2);
        pleaseWaitForm.StartPosition = FormStartPosition.Manual;
        pleaseWaitForm.Location = waitFormLocation;           
    } 

If you have a resizable parent form and you wanted your sub form to also be centered whenever the main form is resized, you should, in theory, be able to place this code in a method and then call the method on both the LocationChanged and SizeChanged events.

jwitt98
  • 1,194
  • 1
  • 16
  • 30
2

JYelton's answer worked for me, but the form is only centered the first time Show() is called. If you want to Hide() the form, and then have it re-centered on the parent every time Show() is called you need use the following in your form:

public new void Show(IWin32Window owner)
{
    base.Show(owner);

    if (Owner != null)
        Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2,
            Owner.Location.Y + Owner.Height / 2 - Height / 2);
}
Powelly
  • 61
  • 7
2

Maybe this can help somebody.

Form frmMessage = new Form();

From experience, although they look similar, they behave different:

This variant doesn't work:

if (frmMessage.Parent != null)
    frmMessage.CenterToParent();
else
    frmMessage.CenterToScreen();

And this variant works

if (frmMessage.Parent != null)
    frmMessage.StartPosition = FormStartPosition.CenterParent;
else
    frmMessage.StartPosition = FormStartPosition.CenterScreen;
Gogu CelMare
  • 699
  • 6
  • 15
1

Using

form.Show(this);

throws an exception if you call it a second time. Manually setting the location seems to be the only reliable option :/ (wasn't it fairly recently that CenterParent used to work?)

DanW
  • 1,976
  • 18
  • 14
1

just put the code in the constructor of your form.

    public FrmSample()
    {
        InitializeComponent();

        // must be after the InitializeComponent()
        this.StartPosition = FormStartPosition.CenterParent;
    }
0

Small Change to JYelton's answer

Form2_Load(object sender, EventArgs e)
{
    if (Owner != null && Parent == null && StartPosition == FormStartPosition.CenterParent)
    Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2,
        Owner.Location.Y + Owner.Height / 2 - Height / 2);
}
D.J.
  • 87
  • 2
  • 2
0

An old question, I know, but I had the same issue but for a different reason.

The Form I was opening had an overridden OnLoad method:

protected override void OnLoad(EventArgs e)
{
   //... etc.
}

but was not calling the base implementation as it must do:

protected override void OnLoad(EventArgs e)
{
   //... etc.
   base.OnLoad(e);
}

When overriding OnLoad(EventArgs) in a derived class, be sure to call the base class's OnLoad(EventArgs) method so that registered delegates receive the event.

oatsoda
  • 2,088
  • 2
  • 26
  • 49
0

Building off the answer by deerchao,

if (form.StartPosition == FormStartPosition.CenterParent) {
    form.Location = new Point(Location.X + (Width - form.Width) / 2, Location.Y + (Height - form.Height) / 2);
}

Do this after form.Show(), this will work on multi-monitors also