0

I am making my program multi-threaded because it is a server manager for Bukkit (Minecraft) which is pretty heavy for some systems. Now I am facing a little problem. When I want to open a new child form I can't tell it to centerparent. It just doesn't do what I tell the form to do. I have found a workaround for this but I don't know if it can be done more easy or if I have to change something else.

This is the code I have atm:
Works

private void ThreadFrmSettings()
{
    Form frmSettings = new frmSettings();
    frmSettings.StartPosition = FormStartPosition.Manual;
    frmSettings.Location = new Point(Location.X + (Width - frmSettings.Width) / 2, Location.Y + (Height - frmSettings.Height) / 2);
    frmSettings.ShowDialog();
}

Doesnt work

var frmSettings = new frmSettings();
frmSettings.ShowDialog();

Is this behavior normal? Thanks!

STW
  • 44,917
  • 17
  • 105
  • 161
Yuki Kutsuya
  • 3,968
  • 11
  • 46
  • 63
  • What about `StartPosition = FormStartPosition.CenterScreen;` property of the `From`? – NoWar Mar 21 '13 at 18:30
  • 2
    What about the multi-threading is bothering you particularly? The default behavior for "ShowDialogue" is not the "centerparent", whether or not you have multiple threads. – IdeaHat Mar 21 '13 at 18:35
  • @MadScienceDreams When I remove the multi-threading the code works perfectly :/. – Yuki Kutsuya Mar 21 '13 at 18:38
  • possible duplicate of [FormStartPosition.CenterParent does not work](http://stackoverflow.com/questions/8567058/formstartposition-centerparent-does-not-work) – Peter Ritchie Mar 21 '13 at 18:40
  • you're not telling frmSettings about a parent. – Peter Ritchie Mar 21 '13 at 18:41
  • @PeterRitchie Shouldn't it automatically know because of the `ShowDialog`? – Yuki Kutsuya Mar 21 '13 at 18:42
  • Lots of ways to blow your leg off trying to display a window on a worker thread. This is a mild one, the window doesn't have a parent. – Hans Passant Mar 21 '13 at 18:42
  • @HansPassant How do I assign a parent to the form when opening in another thread? – Yuki Kutsuya Mar 21 '13 at 18:43
  • A parent should belong to the same thread. You can force it by pinvoking SetParent(). But that will turn the window in a zombie when the thread that owns that parent window isn't pumping a message loop. The typical reason to create a window on another thread in the first place. You therefore should not do this. Just set its Location property yourself. – Hans Passant Mar 21 '13 at 18:48
  • @HansPassant So the code I have right now is the best way to go? – Yuki Kutsuya Mar 21 '13 at 18:50
  • @FoxyShadoww If you used the `ShowDialog(IWin32Window)` override (e.g. `frmSettings.ShowDialog(someParent);`) it would. Otherwise, it will take the "main" (or first) form from the current thread. If there are none, it will think there is no parent. I'm not sure how WinForms will handle a parent from a different thread though, so, be warned that might fail. – Peter Ritchie Mar 21 '13 at 19:03
  • The notion of "best way" is off the table when you run a window on another thread. You probably forgot about other stuff, like calling Thread.SetApartmentState() to force an STA. – Hans Passant Mar 21 '13 at 19:04
  • And no, ShowDialog(owner) will bomb with an InvalidOperationException. – Hans Passant Mar 21 '13 at 19:05
  • @HansPassant Yea, it looks like it :(. – Yuki Kutsuya Mar 21 '13 at 19:06
  • I would probably do 'this.Invoke(new MethodInvoker(()=>ThreadFrmSettings())', which will get around this headache by asynchronously borrowing the main messaging thread. – IdeaHat Mar 21 '13 at 19:16
  • @MadScienceDreams This doesn't give any issues later on? – Yuki Kutsuya Mar 21 '13 at 19:22
  • @FoxyShadoww nope, and I think it is actually the "correct" way to handle this: [all UI updates should be on the main thread, as windows forms are not thread safe](http://msdn.microsoft.com/en-us/library/ms171728%28v=vs.80%29.aspx). Note: "ShowDialogue" will block the main form. If that is not the behavior you want, you should use the "show" call. There are some instances where you want the different forms to have their own messaging threads, like if you are trying to do processor intensive display tasks, but you'll get more bang for your buck using DirectX/OpenGL controls. – IdeaHat Mar 21 '13 at 20:30

2 Answers2

0

What about StartPosition = FormStartPosition.CenterScreen; property of the From?

I.e.

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        StartPosition = FormStartPosition.CenterScreen;

        Form frmSettings = new Form();
        frmSettings.Width = 300;
        frmSettings.Height = 200;
        frmSettings.StartPosition = FormStartPosition.CenterScreen;
        //frmSettings.Location = new Point(Location.X + (Width - frmSettings.Width) / 2, Location.Y + (Height - frmSettings.Height) / 2);
        frmSettings.ShowDialog();

    }
}

Note: frmSettings.Show(); gives different result and in that case window is not going to be at the center.

Here is the sample with a thread/task

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            StartPosition = FormStartPosition.CenterScreen;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew(() => {

                Form frmSettings = new Form();
                frmSettings.Width = 300;
                frmSettings.Height = 200;
                frmSettings.StartPosition = FormStartPosition.CenterParent;

                frmSettings.ShowDialog();

            } );   

        }
    }

enter image description here

NoWar
  • 36,338
  • 80
  • 323
  • 498
0

I've followed MadScienceDreams's advice and got the result I wanted, this is the code that did the trick:

this.Invoke(new MethodInvoker(()=>ThreadFrmSettings())
Yuki Kutsuya
  • 3,968
  • 11
  • 46
  • 63
  • `MethodInvoker` from [Microsoft.JScript](http://msdn.microsoft.com/en-us/library/microsoft.jscript.methodinvoker.aspx)? That page says "This API supports the .NET Framework infrastructure and is not intended to be used directly from your code." – Jeff B Jun 10 '13 at 21:26