设置OpenFileDialog / SaveFileDialog的起始位置

时间:2009-08-10 17:25:10

标签: c# winforms openfiledialog multiple-monitors

对于WinForm应用程序中的任何自定义对话框(窗体),我可以在显示它之前设置其大小和位置:

form.StartPosition = FormStartPosition.Manual;
form.DesktopBounds = MyWindowPosition;

在处理多台显示器时,这一点尤为重要。如果没有此类代码,当您从已拖动到第二个监视器的应用程序打开对话框时,对话框将显示在主监视器上。这会带来糟糕的用户体验。

我想知道是否有任何钩子来设置标准.NET OpenFileDialog和SaveFileDialog(没有StartPosition属性)的位置。

8 个答案:

答案 0 :(得分:4)

我怀疑您可以做的最好的事情是确保使用接受IWin32Window的{​​{3}}作为父级。 可能帮助它选择合适的位置;最常见的是:

using(var dlg = new OpenFileDialog()) {
    .... setup
    if(dlg.ShowDialog(this) == DialogResult.OK) {
        .... use
    }
}

答案 1 :(得分:4)

在CodeProject上查看this article。摘录:

  

这是方便的.NET   NativeWindow进入了画面,a   NativeWindow是一个窗口包装器   它处理由...发送的消息   与之相关的句柄。它创造了一个   NativeWindow和关联   OpenFileWindow处理它。由此   点,发送给每条消息   OpenFileWindow将被重定向到   我们自己的WndProc方法   相反,NativeWindow,我们可以   取消,修改或让它们通过   通过

     

在我们的WndProc中,我们处理消息   WM_WINDOWPOSCHANGING。如果开放   对话框正在打开,然后我们将改变   原始的水平或垂直   大小取决于StartLocation   由用户设置。它会增加   要创建的窗口的大小。这个   控制时只发生一次   打开。

     

此外,我们将处理该消息   WM_SHOWWINDOW。在这里,所有控件   在原来的OpenFileDialog里面   创建了,我们将要追加   我们对打开文件对话框的控制。   这是通过调用Win32 API完成的   的setparent。此API允许您更改   父窗口。然后,基本上   它的作用是附加我们的控制   到原来的OpenFileDialog中   它设置的位置,取决于   StartLocation属性的值。

     

它的优点是我们仍然   完全控制了   控制附在   OpenFileDialog窗口。这意味着我们   可以接收事件,呼叫方法和   做任何我们想要的东西   控件。

答案 2 :(得分:2)

我昨天的大部分时间都有这个问题。 BobB的答案是最能帮助我的人(感谢BobB)。

您甚至可以创建一个私有方法来创建窗口并在dialog.ShowDialog()方法调用之前将其关闭,它仍然会使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;
}

然后在ShowDialog()方法之前以任何方法调用它。

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;
}

答案 3 :(得分:1)

我是这样做的:

我想要显示OpenFileDialog的地方:

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

DialogResult dr = ofd.ShowDialog();

重新定位代码:

[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);
}

我启动一个寻找带有“Open”标题的窗口的线程。 (通常在3次迭代或15毫秒内找到。)然后我用获得的句柄设置它的位置。 (有关位置/大小参数,请参阅SetWindowPos文档。)

缺憾。

答案 4 :(得分:1)

OpenFileDialog和SaveFileDialog将自己定位在左上角 最近显示的窗口的客户区。因此,只需创建一个新的不可见窗口,在创建和显示该对话框之前,您希望对话框出现在该窗口中。

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();

答案 5 :(得分:0)

在MSDN上有一个很老的例子就是一种方法。

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

它包含了实现允许可扩展性的自己的OpenFileDialog类所需的所有代码。

答案 6 :(得分:0)

非常感谢BobB对此的回复。还有一些“陷阱”。调用OpenFileDialog1.ShowDialog(PositionForm)时必须传递PositionForm的句柄,否则BobB的技术在所有情况下都不可靠。此外,现在W8.1启动了一个带有SkyDrive的新文件打开控件,W8.1 fileopen控件中的Documents文件夹位置现在已拧紧。所以我通过设置ShowHelp = True来frig fileopen使用旧的W7控件。

这是我最终使用的VB.NET代码,如果它有帮助,我对社区的贡献。

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

答案 7 :(得分:0)

以Rob Sherrit在14年1月22日的回应为灵感,我创建了一个新模块,并将其命名为CKRFileDialog(称其为您想要的),其中包含以下代码:

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

然后我在各个模块中按以下方式调用此代码:

如果执行“ FileOpen”,请确保在您的表单或代码中添加了FileOpenDialog组件,并根据需要调整该组件的属性。 (例如InitDirectory,Multiselect等)

在使用FileSaveDialog组件时执行相同的操作(可能会使用与FileOpenDialog组件不同的属性)。

要“显示”对话框组件,请使用以下代码行,并传递两个参数,第一个是您正在使用的FileDialog(“打开”或“保存”),第二个参数是您希望覆盖的窗体对话。

CKRFileDialog.Show(saveFileDialog1,CoveredForm) 要么 CKRFileDialog.Show(openFileDialog1,CoveredForm)

请记住,如果您使用的是SkyDrive,则必须传递“ True”作为第三个参数:

CKRFileDialog.Show(saveFileDialog1,CoveredForm,True) 要么 CKRFileDialog.Show(openFileDialog1,CoveredForm,True)

我将对话框的“偏移”设置为在表单上下移动的1/8 “ CoveredForm”,但您可以将其设置回1/2(如Rob Sherret的代码)或所需的任何值。

这似乎是最简单的方法

谢谢罗伯! :-)