2009-08-10 168 views
20

对于在WinForm应用程序我可以设置其大小和位置的任何定制对话框(形式)之前,我用显示它:设置开始位置的OpenFileDialog/SaveFileDialog

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

具有多个处理时,这是特别重要显示器。如果没有这样的代码,当您从拖动到第二个监视器的应用程序中打开对话框时,该对话框将显示在主监视器上。这表现出糟糕的用户体验。

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

回答

3

我怀疑你可以做的最好的事情是确保你使用接受IWin32Windowoverload of ShowDialog作为父母。这可能帮助它选择一个合适的位置;最常见的是:

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

这听起来很简单,它必须工作(至少它必须测试)!唉,在这个测试用例中,0-arg和1-arg ShowDialog都以同样的方式失败: 1.运行应用程序。 2.调用新的OpenFileDialog()。ShowDialog(this);对话框与应用程序在相同的监视器上 3.关闭对话框。 4.将应用程序窗口拖到不同的显示器上。调用新的OpenFileDialog()。ShowDialog(this);对话框出现在* original *监视器上。 尽管我在第5步中使用了新鲜的OpenFileDialog,但仍然有一些关于主应用程序的原始位置的信息。 – 2009-08-10 19:31:13

+1

我(最后:-)选择Marc的答案是最好的,因为我最近发现它*不适用于Windows 7.我的机器是WinXP,上面提到的测试用例仍然失败。我决定尝试使用相同问题的Microsoft论坛,并给出了适用于WinXP的解决方案 - 请参阅此主题(http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/dec48489- 0a57-4baa-b401-82266be782e0)代码。 – 2010-07-13 18:27:10

4

在CodeProject上检出this article。摘录:

这里是当得心应手.NET 的NativeWindow到图片,一个 NativeWindow的是一个窗口封装器,其中 它处理由与之相关的 手柄发送的消息。它会创建一个 NativeWindow并将其关联到OpenFileWindow句柄。从这点 ,发送到 OpenFileWindow每个消息将在 的NativeWindow被重定向到 我们自己的WndProc方法来代替,我们可以 取消,修改,或让他们通过 通过。

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

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

它的好处是,我们仍然可以通过 完全控制连接到 OpenFileDialog窗口的 控件。这意味着我们 可以接收事件,呼叫方法和 做任何我们想要的与这些 控件。

0

我是这样做的:

,我想显示的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); 
} 

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

Kludgy。

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

如果不愿意使用'DllImport',就足够了。 – IAbstract 2016-08-19 21:08:54

0

非常感谢BobB对此的回复。还有几个“陷阱”。调用OpenFileDialog1.ShowDialog(PositionForm)时,必须传递PositionForm的句柄,否则BobB的技巧在所有情况下都不可靠。而且,现在W8.1启动了一个新的带有SkyDrive的文件打开控件,W8.1文件打开控件中的文件夹位置现在被拧紧了。因此,我通过设置ShowHelp = True来打开文件以使用旧的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 
1

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