Setting the start position for OpenFileDialog/SaveFileDialog
Asked Answered
C

8

28

For any custom dialog (form) in a WinForm application I can set its size and position before I display it with:

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 dragged to a second monitor, the dialog appears on the primary monitor. This presents a poor user experience.

I am wondering if there are any hooks to set the position for the standard .NET OpenFileDialog and SaveFileDialog (which do not have a StartPosition property).

Clarinda answered 10/8, 2009 at 17:25 Comment(0)
P
6

I suspect that the best you can do is make sure you use the overload of ShowDialog that accepts an IWin32Window to use as the parent. This might help it choose an appropriate location; most commonly:

using(var dlg = new OpenFileDialog()) {
    .... setup
    if(dlg.ShowDialog(this) == DialogResult.OK) {
        .... use
    }
}
Peck answered 10/8, 2009 at 18:30 Comment(2)
This sounded so simple it had to work (at least it had to be tested)! Alas, both the 0-arg and the 1-arg ShowDialog fail in the same way on this test case: 1. Run application. 2. Invoke new OpenFileDialog().ShowDialog(this); dialog appears on same monitor as application. 3. Close dialog. 4. Drag application window to different monitor. 5. Invoke new OpenFileDialog().ShowDialog(this); dialog appears on original monitor. Even though I use a fresh OpenFileDialog in step 5, there is still something persistent about the main app's original location.Clarinda
I am ( finally :-) choosing Marc's answer as the best because I recently found out it does apply to Windows 7. My machine is WinXP where the test case I outline just above still fails. I decided to try the Microsoft forums with the same question and was given a solution that works for WinXP--see this thread (social.msdn.microsoft.com/Forums/en-US/winforms/thread/…) for the code.Clarinda
S
4

Check out this article on CodeProject. Excerpt:

Here is when the handy .NET NativeWindow comes into the picture, a NativeWindow is a window wrapper where it processes the messages sent by the handle associated to it. It creates a NativeWindow and associates the OpenFileWindow handle to it. From this point, every message sent to OpenFileWindow will be redirected to our own WndProc method in the NativeWindow instead, and we can cancel, modify, or let them pass through.

In our WndProc, we process the message WM_WINDOWPOSCHANGING. If the open dialog is opening, then we will change the original horizontal or vertical size depending of the StartLocation set by the user. It will increment the size of the window to be created. This happens only once when the control is opened.

Also, we will process the message WM_SHOWWINDOW. Here, all controls inside the original OpenFileDialog are created, and we are going to append our control to the open file dialog. This is done by calling a Win32 API SetParent. This API lets you change the parent window. Then, basically what it does is attach our control to the original OpenFileDialog in the location it set, depending on the value of the StartLocation property.

The advantage of it is that we still have complete control over the controls attached to the OpenFileDialog window. This means we can receive events, call methods, and do whatever we want with those controls.

Stringy answered 10/8, 2009 at 18:44 Comment(0)
R
4

OpenFileDialog and SaveFileDialog position themselves in the upper-left corner of the client area of the most recently displayed window. So just create a new invisible window positioned where you want the the dialog to appear before creating and showing that dialog.

Window dialogPositioningWindow = new Window();
dialogPositioningWindow.Left = MainWindow.Left + <left position within main window>;
dialogPositioningWindow.Top  = MainWindow.Top  + <top  position within main window>;
dialogPositioningWindow.Width = 0; 
dialogPositioningWindow.Height = 0; 
dialogPositioningWindow.WindowStyle = WindowStyle.None;
dialogPositioningWindow.ResizeMode = ResizeMode.NoResize;
dialogPositioningWindow.Show();// OpenFileDialog is positioned in the upper-left corner
                               // of the last shown window (dialogPositioningWindow)
Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
...
if ((bool)dialog.ShowDialog()){
   ...
}
dialogPositioningWindow.Close();
Rust answered 30/6, 2013 at 13:33 Comment(1)
Adequate if reluctant to use DllImports.Bifacial
A
2

I had this problem for most of yesterday. BobB's answer was the one that helped me out the most (Thanks BobB).

You can even go as far as to make a private method that creates a window and closes it before the dialog.ShowDialog() method call and it will still centre the OpenFileDialog.

private void openFileDialogWindow()
{
    Window openFileDialogWindow = new Window();
    openFileDialogWindow.Left = this.Left;
    openFileDialogWindow.Top = this.Top;
    openFileDialogWindow.Width = 0;
    openFileDialogWindow.Height = 0;
    openFileDialogWindow.WindowStyle = WindowStyle.None;
    openFileDialogWindow.ResizeMode = ResizeMode.NoResize;
    openFileDialogWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;

    openFileDialogWindow.Show();
    openFileDialogWindow.Close();

    openFileDialogWindow = null;
}

Then call it in any method before the ShowDialog() method.

public string SelectWebFolder()
{
    string WebFoldersDestPath = null;

    CommonOpenFileDialog filePickerDialog = new CommonOpenFileDialog();
    // OpenFileDialog Parameters..

    openFileDialogWindow();

    if (filePickerDialog.ShowDialog() == CommonFileDialogResult.Ok)
    {
        WebFoldersDestPath = filePickerDialog.FileName + "\\";
    }

    filePickerDialog = null;

    return WebFoldersDestPath;
}
Alfano answered 16/11, 2016 at 11:40 Comment(0)
T
1

Here's how I did it:

The point where I want to display the OpenFileDialog:

Thread posThread = new Thread(positionOpenDialog);
posThread.Start();

DialogResult dr = ofd.ShowDialog();

The repositioning code:

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);


/// <summary>
/// Find the OpenFileDialog window when it appears, and position it so
/// that we can see both dialogs at once.  There is no easier way to
/// do this (&^%$! Microsoft!).
/// </summary>
private void positionOpenDialog ()
{
    int count = 0;
    IntPtr zero = (IntPtr)0;
    const int SWP_NOSIZE = 0x0001;
    IntPtr wind;

    while ((wind = FindWindowByCaption(zero, "Open")) == (IntPtr)0)
        if (++count > 100)
            return;             // Find window failed.
        else
            Thread.Sleep(5);

    SetWindowPos(wind, 0, Right, Top, 0, 0, SWP_NOSIZE);
}

I start a thread that looks for a window with the "Open" title. (Typically found in 3 iterations or 15 milliseconds.) Then I set its position with the obtained handle. (See SetWindowPos documentation for the position/size parameters.)

Kludgy.

Tutti answered 7/5, 2012 at 21:51 Comment(0)
M
0

There is quite an old example of one approach on MSDN.

http://msdn.microsoft.com/en-us/library/ms996463.aspx

It includes all the code needed to implement your own OpenFileDialog class that allows extensibility.

Marj answered 10/8, 2009 at 18:52 Comment(0)
S
0

Very grateful for BobB's reply on this one. There are a few more "gotchas". You have to pass the handle of PositionForm when calling OpenFileDialog1.ShowDialog(PositionForm) otherwise BobB's technique is not reliable in all cases. Also, now that W8.1 launches a new fileopen control with SkyDrive in it, the Documents folder location in the W8.1 fileopen control is now screwed. So I frig fileopen to use the old W7 control by setting ShowHelp = True.

Here is the VB.NET code I ended up using, my contribution to the community in case it helps.

Private Function Get_FileName() As String

    ' Gets an Input File Name from the user, works with multi-monitors

    Dim OpenFileDialog1 As New OpenFileDialog
    Dim PositionForm As New Form
    Dim MyInputFile As String

    ' The FileDialog() opens in the last Form that was created.  It's buggy!  To ensure it appears in the
    ' area of the current Form, we create a new hidden PositionForm and then delete it afterwards.

    PositionForm.StartPosition = FormStartPosition.Manual
    PositionForm.Left = Me.Left + CInt(Me.Width / 2)
    PositionForm.Top = Me.Top + CInt(Me.Height / 2)
    PositionForm.Width = 0
    PositionForm.Height = 0
    PositionForm.FormBorderStyle = Forms.FormBorderStyle.None
    PositionForm.Visible = False
    PositionForm.Show()

    ' Added the statement "ShowHelp = True" to workaround a problem on W8.1 machines with SkyDrive installed.
    ' It causes the "old" W7 control to be used that does not point to SkyDrive in error.

    OpenFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
    OpenFileDialog1.Filter = "Excel files (*.xls*)|*.xls*|CSV Files (*.csv)|*.csv"
    OpenFileDialog1.FilterIndex = 1
    OpenFileDialog1.RestoreDirectory = True
    OpenFileDialog1.AutoUpgradeEnabled = False
    OpenFileDialog1.ShowHelp = True
    OpenFileDialog1.FileName = ""
    OpenFileDialog1.SupportMultiDottedExtensions = False
    OpenFileDialog1.Title = "Select an Excel or .csv file containing patent data or list of Publication Numbers for your project."

    If OpenFileDialog1.ShowDialog(PositionForm) <> System.Windows.Forms.DialogResult.OK Then
        Console.WriteLine("No file was selected. Please try again!")
        PositionForm.Close()
        PositionForm.Dispose()
        OpenFileDialog1.Dispose()
        Return ""
    End If
    PositionForm.Close()
    PositionForm.Dispose()

    MyInputFile = OpenFileDialog1.FileName
    OpenFileDialog1.Dispose()
    Return MyInputFile

End Function
Sika answered 22/1, 2014 at 20:13 Comment(0)
S
0

Using Rob Sherrit's response on Jan 22 '14 as inspiration, I created a new module and called it CKRFileDialog (call it what you want) which contains the following code:

Public Function Show(fd As Object, CoveredForm As Form, Optional bShowHelp As Boolean = False) As DialogResult

    Dim oDR As DialogResult

    'The .Net FileDialogs open in the last Form that was created. 
    'To ensure they appear in the area of the current Form, we create a new HIDDEN PositionForm and then 
    'delete it afterwards.

    Dim PositionForm As New Form With {
      .StartPosition = FormStartPosition.Manual,
      .Left = CoveredForm.Left + CInt(CoveredForm.Width / 8),  'adjust as required
      .Top = CoveredForm.Top + CInt(CoveredForm.Height / 8),   'adjust as required
      .Width = 0,
      .Height = 0,
      .FormBorderStyle = Windows.Forms.FormBorderStyle.None,
      .Visible = False
    }
    PositionForm.Show()

    'If you use SkyDrive you need to ensure that "bShowHelp" is set to True in the passed parameters.
    'This is a workaround for a problem on W8.1 machines with SkyDrive installed.
    'Setting it to "true" causes the "old" W7 control to be used which avoids a pointing to SkyDrive error.
    'If you do not use SkyDrive then simply do not pass the last parameter (defaults to "False")
    fd.ShowHelp = bShowHelp

    'store whether the form calling this routine is set as "topmost"
    Dim oldTopMost As Integer = CoveredForm.TopMost
    'set the calling form's topmost setting to "False" (else the dialogue will be "buried"
    CoveredForm.TopMost = False

    oDR = fd.ShowDialog(PositionForm)

    'set the "topmost" setting of the calling form back to what it was.
    CoveredForm.TopMost = oldTopMost
    PositionForm.Close()
    PositionForm.Dispose()
    Return oDR

End Function

I then call this code in my various modules as follows:

If performing a "FileOpen" ensure that there is a FileOpenDialog component added to your form or code and adjust the properties of the component if you wish (e.g. InitDirectory,Multiselect,etc.)

Do the same when using FileSaveDialog components (Different properties to the FileOpenDialog component may apply).

To "show" the dialog component use a line of code as follows, passing two parameters, the first the FileDialog you are using ("Open" or "Save") and the second parameter the Form upon which you wish to overlay the dialogue.

CKRFileDialog.Show(saveFileDialog1, CoveredForm) or CKRFileDialog.Show(openFileDialog1, CoveredForm)

Remember, if you are using SkyDrive you must pass "True" as a third parameter:

CKRFileDialog.Show(saveFileDialog1, CoveredForm, True) or CKRFileDialog.Show(openFileDialog1, CoveredForm, True)

I set the "offset" of the dialogue to be 1/8 the way across and down on the form "CoveredForm", but you can set that back to 1/2 (as in Rob Sherret's code) or whatever value you wish.

This seemed the easiest approach

Thanks Rob! :-)

Suet answered 14/6, 2020 at 10:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.