none
How to make Open/Save file dialog open on same monitor as app with multiple monitors

    Question

  • For any custom dialog (form) in a WinForm application one can set its size and position before displaying it with code such as this:

     

    form.StartPosition = FormStartPosition.Manual;
    form.DesktopBounds = MyWindowPosition;
    
    
    This is particularly important when dealing with multiple monitors. Without such code, when you open a dialog from an application that you have opened on a second monitor, the dialog appears on the primary monitor. This presents a poor user experience.

    I would like to know how to do this for standard .NET dialogs, particularly OpenFileDialog and SaveFileDialog (which do not provide a StartPosition property).

    I have been looking for a good solution to this for awhile, even posting on other forums some time ago, but have had no success resolving this question.

    Friday, June 25, 2010 10:04 PM

Answers

  • If you are really really desperate you can resort to the Win32 API...
    (Yes it's ugly but it works.)

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Runtime.InteropServices;
    
    namespace MultiMonitorTest
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }
    
        private void OpenDialogButton_Click(object sender, EventArgs e)
        {
          // e.g. place over top of the main form
          Point myNewLocation = Location; 
          myNewLocation.Offset(20, 20);
    
          OpenFileDialog dlg = new OpenFileDialog();
          dlg.Title = "Open";
          MoveDialogWhenOpened(dlg.Title, myNewLocation);
          dlg.ShowDialog(this);
        }
    
        private void SaveFileDialogButton_Click(object sender, EventArgs e)
        {
          // e.g. place over top of the main form
          Point myNewLocation = Location; 
          myNewLocation.Offset(20, 20);
    
          SaveFileDialog dlg = new SaveFileDialog();
          dlg.Title = "Save As";
          MoveDialogWhenOpened(dlg.Title, myNewLocation);
          dlg.ShowDialog(this);
        }
    
        private void MoveDialogWhenOpened(String windowCaption, Point location)
        {
          Object[] argument = new Object[] { windowCaption, location };
    
          BackgroundWorker backgroundWorker = new BackgroundWorker();
          backgroundWorker.DoWork += new DoWorkEventHandler(MoveDialogThread);
          backgroundWorker.RunWorkerAsync(argument);
        }
    
        private void MoveDialogThread(Object sender, DoWorkEventArgs e)
        {
          const String DialogWindowClass = "#32770";
    
          String windowCaption = (String)(((Object[])e.Argument)[0]);
          Point location = (Point)(((Object[])e.Argument)[1]);
    
          // try for a maximum of 4 seconds (sleepTime * maxAttempts)
          Int32 sleepTime = 10; // milliseconds
          Int32 maxAttempts = 400;
    
          for (Int32 i = 0; i < maxAttempts; ++i)
          {
            // find the handle to the dialog
            IntPtr handle = Win32Api.FindWindow(DialogWindowClass, windowCaption);
    
            // if the handle was found and the dialog is visible
            if ((Int32)handle > 0 && Win32Api.IsWindowVisible(handle) > 0)
            {
              // move it
              Win32Api.SetWindowPos(handle, (IntPtr)0, location.X, location.Y,
                         0, 0, Win32Api.SWP_NOSIZE | Win32Api.SWP_NOZORDER);
              break;
            }
    
            // if not found wait a brief sec and try again
            Thread.Sleep(sleepTime);
          }
        }
      }
    
      public class Win32Api
      {
        public const Int32 SWP_NOSIZE = 0x1;
        public const Int32 SWP_NOZORDER = 0x4;
    
        [DllImport("user32")]
        public static extern IntPtr FindWindow(String lpClassName, 
                            String lpWindowName);
    
        [DllImport("user32")]
        public static extern Int32 IsWindowVisible(IntPtr hwnd);
    
        [DllImport("user32")]
        public static extern Int32 SetWindowPos(IntPtr hwnd, 
                            IntPtr hwndInsertAfter, 
                            Int32 x, 
                            Int32 y, 
                            Int32 cx, 
                            Int32 cy, 
                            Int32 wFlags);
      }
    }
    

    • Marked as answer by msorens Tuesday, July 13, 2010 6:13 PM
    Thursday, July 08, 2010 8:25 PM

All replies

  • Hi msorens,

     

    What about showing the dialog like this?

    OpenFileDialog openFileDlg = new OpenFileDialog();

    openFileDlg.ShowDialog(this);

     

    “this” refers to the parent form. The OpenFileDialog will always popup on the parent form. I do not have multiple screens to test it. Please test it and tell me if it works?

     

    Sincerely,

    Kira Qian

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    • Proposed as answer by _xr280xr_ Monday, June 28, 2010 10:14 PM
    • Unproposed as answer by msorens Tuesday, June 29, 2010 5:39 PM
    Monday, June 28, 2010 2:46 AM
  • That is the obvious thing to try, of course, but it does not work. Here is a scenario where it fails:

    1. Run application.
    2. Invoke new OpenFileDialog().ShowDialog(this) to open dialog; the  dialog appears on the same monitor as the application.
    3. Close dialog.
    4. Drag application window to different monitor.
    5. Invoke new OpenFileDialog().ShowDialog(this); dialog appears on original monitor.


    Even using a fresh OpenFileDialog in step 5, there is still something persistent about the main app's original location. In contrast, the same test on a custom dialog window succeeds, where the custom dialog (form) has StartPosition and DesktopBounds set as in my original question.

     

    Tuesday, June 29, 2010 5:45 PM
  • > Even using a fresh OpenFileDialog in step 5, there is still something persistent about the main app's original location.

     

    In order to test your issue, I found a multi-monitor pc to do the test.

     

    1. If you use a fresh Dialog to show. It appears on the secondary monitor when the parent form is there. If you try to drag the OpenFileDialog to the main monitor and close. The next time it will open in main monitor.

     

    2. If you do not create a new instance of OpenFileDialog, it will remember the original location.

     

    3. A standard Form does not like OpenFileDialog. It allows you to derive and change most property. The OpenFileDialog is not. It come from Windows system.

     

    Sincerely,

    Kira Qian

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    Wednesday, June 30, 2010 2:14 AM
  • I think you are trying to say that the OpenFileDialog does not have the same properties as custom dialogs so the technique in my original question does not apply, which I already know. My original question was, and still is, to find another solution to the problem.

    First, I will state that there should be a solution because if a piece of software does not do what a user reasonably expects then it has a defect that should be corrected. I maintain that if I move my application from monitor A to monitor B then bring up an OpenFileDialog, it is reasonable to assume the dialog will be on the same monitor.

    Second, there are a variety of articles available on the web that customize the OpenFileDialog. My only problem is that from the several I have looked at it is not clear to me how to address my specific customization, i.e. open on the same monitor.

    Wednesday, June 30, 2010 2:55 PM
  • Hi msorens,

     

    > it is reasonable to assume the dialog will be on the same monitor.

     

    I quite agree with that. But the situation is it already display on the same monitor as the form. Please new an instance when you want to show the OpenFileDialog. Do not use the original one. If the situation on your side is not the same as mine, could you please make a simple project to reproduce the issue?

     

    Sincerely,

    Kira Qian

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    Thursday, July 01, 2010 1:57 AM
  • I suspect you did not try the test case I detailed in my June 29th reply. I am confident that if you had you would have seen the problem with your own test. Nevertheless, here is a complete program ready for testing and again here are the steps to show the problem:

     

    1. Run application.
    2. Press button to open dialog; the  dialog appears on the same monitor as the application.
    3. Close dialog.
    4. Drag application window to different monitor.
    5. Press button to open dialog; dialog appears on original monitor.

    using System;
    using System.Windows.Forms;
    
    namespace MultiMonitorTest
    {
      static class Program
      {
        [STAThread]
        static void Main()
        {
          Application.EnableVisualStyles();
          Application.Run(new Form1());
        }
      }
    
      public class Form1 : Form
      {
        private System.ComponentModel.IContainer components = null;
        private System.Windows.Forms.Button button1;
    
        private void InitializeComponent()
        {
          this.components = new System.ComponentModel.Container();
          this.button1 = new System.Windows.Forms.Button();
          this.button1.Location = new System.Drawing.Point(86, 111);
          this.button1.Text = "button1";
          this.button1.Click += new System.EventHandler(this.button1_Click);
          this.ClientSize = new System.Drawing.Size(356, 281);
          this.Controls.Add(this.button1);
        }
    
        public Form1() {InitializeComponent();}
    
        private void button1_Click(object sender, EventArgs e)
        {
          OpenFileDialog dlg = new OpenFileDialog();
          dlg.ShowDialog(this);
        }
      }
    }
    
    Thursday, July 01, 2010 3:37 PM
  • I have already followed these steps to do the test before I post my reply. The result is

    4. Drag application window to different monitor.

    5. Press button to open dialog; dialog appears on different monitor.

     

    The code is the same as yours. That’s why I said I cannot reproduce the issue.

     

    Sincerely,

    Kira Qian

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    Friday, July 02, 2010 1:41 AM
  • I suspect you did not try the test case I detailed in my June 29th reply. I am confident that if you had you would have seen the problem with your own test. 

    Please give some credit and show some respect for the people who are trying to help you here ... 

    I can confirm that the steps you described are not enough to reproduce the issue on my machine either. even with playing around, using the same instance of dialog, adding a main form which starts the other one ... nothing helped.

    Did you have a chance to experience the same issue on another system, or have complaints from users ?

    BTW, I've been testing on Windows Vista Business SP1 ... 

    Best regards,
    Vladimir

    Saturday, July 03, 2010 9:39 AM
  • Vladimir: You brought up a key point that I was about to comment on as well: the OS. My simple test fails on every machine I have tried--all are running XP. And I have tried compiling against .NET 2.0 and against .NET 3.5.

    While I thought it a lot more likely that the .NET version might be a factor rather than the OS, apparently not.

    Kira, what OS did you test with?

     

    Tuesday, July 06, 2010 9:45 PM
  • I use Win7 and Server 2008.

    Sincerely,

    Kira Qian

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    Wednesday, July 07, 2010 1:46 AM
  • So my original issue still remains, with a slight revision:

    How do I get OpenFileDialog and SaveFileDialog to always open on the same monitor as its parent app--from WinXP through Win7?

    Wednesday, July 07, 2010 3:28 PM
  • Sorry, there is no solution for WinXP.

     

    Sincerely,

    Kira Qian

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    Thursday, July 08, 2010 1:26 AM
  • If you are really really desperate you can resort to the Win32 API...
    (Yes it's ugly but it works.)

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Runtime.InteropServices;
    
    namespace MultiMonitorTest
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }
    
        private void OpenDialogButton_Click(object sender, EventArgs e)
        {
          // e.g. place over top of the main form
          Point myNewLocation = Location; 
          myNewLocation.Offset(20, 20);
    
          OpenFileDialog dlg = new OpenFileDialog();
          dlg.Title = "Open";
          MoveDialogWhenOpened(dlg.Title, myNewLocation);
          dlg.ShowDialog(this);
        }
    
        private void SaveFileDialogButton_Click(object sender, EventArgs e)
        {
          // e.g. place over top of the main form
          Point myNewLocation = Location; 
          myNewLocation.Offset(20, 20);
    
          SaveFileDialog dlg = new SaveFileDialog();
          dlg.Title = "Save As";
          MoveDialogWhenOpened(dlg.Title, myNewLocation);
          dlg.ShowDialog(this);
        }
    
        private void MoveDialogWhenOpened(String windowCaption, Point location)
        {
          Object[] argument = new Object[] { windowCaption, location };
    
          BackgroundWorker backgroundWorker = new BackgroundWorker();
          backgroundWorker.DoWork += new DoWorkEventHandler(MoveDialogThread);
          backgroundWorker.RunWorkerAsync(argument);
        }
    
        private void MoveDialogThread(Object sender, DoWorkEventArgs e)
        {
          const String DialogWindowClass = "#32770";
    
          String windowCaption = (String)(((Object[])e.Argument)[0]);
          Point location = (Point)(((Object[])e.Argument)[1]);
    
          // try for a maximum of 4 seconds (sleepTime * maxAttempts)
          Int32 sleepTime = 10; // milliseconds
          Int32 maxAttempts = 400;
    
          for (Int32 i = 0; i < maxAttempts; ++i)
          {
            // find the handle to the dialog
            IntPtr handle = Win32Api.FindWindow(DialogWindowClass, windowCaption);
    
            // if the handle was found and the dialog is visible
            if ((Int32)handle > 0 && Win32Api.IsWindowVisible(handle) > 0)
            {
              // move it
              Win32Api.SetWindowPos(handle, (IntPtr)0, location.X, location.Y,
                         0, 0, Win32Api.SWP_NOSIZE | Win32Api.SWP_NOZORDER);
              break;
            }
    
            // if not found wait a brief sec and try again
            Thread.Sleep(sleepTime);
          }
        }
      }
    
      public class Win32Api
      {
        public const Int32 SWP_NOSIZE = 0x1;
        public const Int32 SWP_NOZORDER = 0x4;
    
        [DllImport("user32")]
        public static extern IntPtr FindWindow(String lpClassName, 
                            String lpWindowName);
    
        [DllImport("user32")]
        public static extern Int32 IsWindowVisible(IntPtr hwnd);
    
        [DllImport("user32")]
        public static extern Int32 SetWindowPos(IntPtr hwnd, 
                            IntPtr hwndInsertAfter, 
                            Int32 x, 
                            Int32 y, 
                            Int32 cx, 
                            Int32 cy, 
                            Int32 wFlags);
      }
    }
    

    • Marked as answer by msorens Tuesday, July 13, 2010 6:13 PM
    Thursday, July 08, 2010 8:25 PM
  • Well I would not quite characterize myself as desperate :-) regarding this; perhaps passionate: the situation that your code has remedied, Brett, is uglier than your code itself :-) so I consider it a net gain (i.e. I think it reflects poorly on an application that may have its open file dialog potentially a couple feet away on a second or third monitor. At least it apparently is not an issue on Windows 7...  Thanks for your assistance!
    Tuesday, July 13, 2010 6:18 PM
  • Brett Mc, I'd like to use your source code in a project I'm working on.  Can you contact me as user nlyons at Google's email service (forgive me for not entering my email for the spammers to grab).  Thanks!
    5 hours 21 minutes ago