C# 设置 OpenFileDialog/SaveFileDialog 的起始位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1256130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:18:00  来源:igfitidea点击:

Setting the start position for OpenFileDialog/SaveFileDialog

c#winformsopenfiledialogmultiple-monitors

提问by Michael Sorens

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

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

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).

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

采纳答案by Marc Gravell

I suspect that the best you can do is make sure you use the overload of ShowDialogthat accepts an IWin32Windowto use as the parent. This mighthelp it choose an appropriate location; most commonly:

我怀疑你能做的最好的事情就是确保你使用接受 an的重载ShowDialogIWin32Window作为父级。这可能有助于它选择合适的位置;最常见的:

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

回答by Charlie

Check out this articleon CodeProject. Excerpt:

查看关于 CodeProject 的这篇文章。摘抄:

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.

这是当方便的 .NET NativeWindow 出现时,NativeWindow 是一个窗口包装器,它处理由与其关联的句柄发送的消息。它创建一个 NativeWindow 并将 OpenFileWindow 句柄关联到它。从这一点来看,发送到 OpenFileWindow 的每条消息都将被重定向到我们自己在 NativeWindow 中的 WndProc 方法,我们可以取消、修改或让它们通过。

在我们的 WndProc 中,我们处理消息 WM_WINDOWPOSCHANGING。如果打开的对话框正在打开,那么我们将根据用户设置的 StartLocation 更改原始水平或垂直大小。它将增加要创建的窗口的大小。这仅在打开控件时发生一次。

此外,我们将处理消息 WM_SHOWWINDOW。在这里,原始 O​​penFileDialog 中的所有控件都被创建,我们将把我们的控件附加到打开文件对话框中。这是通过调用 Win32 API SetParent 来完成的。此 API 可让您更改父窗口。然后,基本上它所做的是将我们的控件附加到它设置的位置中的原始 OpenFileDialog,具体取决于 StartLocation 属性的值。

它的优点是我们仍然可以完全控制附加到 OpenFileDialog 窗口的控件。这意味着我们可以使用这些控件接收事件、调用方法以及做任何我们想做的事情。

回答by andynormancx

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

在 MSDN 上有一个很古老的例子。

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

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

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

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

回答by user20493

Here's how I did it:

这是我如何做到的:

The point where I want to display the OpenFileDialog:

我想显示 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.)

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

Kludgy.

笨拙。

回答by BobB

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.

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

回答by Rob Sherratt

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.

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

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

这是我最终使用的 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

回答by Iwan Morgan

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

我昨天大部分时间都遇到了这个问题。BobB 的回答对我帮助最大(感谢 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.

您甚至可以创建一个私有方法来创建一个窗口并在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;
}

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

然后在方法之前的任何方法中调用它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;
}