24

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

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

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

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

4

8 回答 8

5

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

using(var dlg = new OpenFileDialog()) {
    .... setup
    if(dlg.ShowDialog(this) == DialogResult.OK) {
        .... use
    }
}
于 2009-08-10T18:30:46.967 回答
4

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

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

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

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

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

于 2009-08-10T18:44:46.547 回答
3

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();
于 2013-06-30T13:33:10.843 回答
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;
}
于 2016-11-16T11:40:48.197 回答
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);
}

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

克鲁吉。

于 2012-05-07T21:51:03.783 回答
0

MSDN 上有一个相当古老的一种方法示例。

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

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

于 2009-08-10T18:52:44.597 回答
0

非常感谢 BobB 对此的回复。还有一些“陷阱”。调用 OpenFileDialog1.ShowDialog(PositionForm) 时必须传递 PositionForm 的句柄,否则 BobB 的技术在所有情况下都不可靠。此外,现在 W8.1 启动了一个包含 SkyDrive 的新文件打开控件,W8.1 文件打开控件中的 Documents 文件夹位置现在搞砸了。所以我通过设置 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
于 2014-01-22T20:13:00.517 回答
0

以 Rob Sherrit 在 2014 年 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)

我将对话的“偏移量”设置为“CoveredForm”表格上上下的 1/8,但您可以将其设置回 1/2(如 Rob Sherret 的代码中)或您希望的任何值。

这似乎是最简单的方法

谢谢罗伯!:-)

于 2020-06-14T10:42:48.883 回答