设置OpenFileDialog / SaveFileDialog的起始位置

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

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

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

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

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

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

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

下面是方便的.NET NativeWindow进入图片时,NativeWindow是一个窗口包装器,它处理由与之关联的句柄发送的消息。 它创建一个NativeWindow并将OpenFileWindow句柄与它相关联。 从这一点开始,发送到OpenFileWindow的每条消息都将被重定向到NativeWindow中我们自己的WndProc方法,我们可以取消,修改或让它们通过。

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

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

它的优点是我们仍然可以完全控制附加到OpenFileDialog窗口的控件。 这意味着我们可以接收事件,调用方法,并使用这些控件执行任何操作。

我昨天的大部分时间都有这个问题。 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; } 

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

 Window dialogPositioningWindow = new Window(); dialogPositioningWindow.Left = MainWindow.Left + ; dialogPositioningWindow.Top = MainWindow.Top + ; 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(); 

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

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

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

非常感谢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 

我是这样做的:

我要显示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); ///  /// 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!). ///  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); } 

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

缺憾。