【问题标题】:Setting the start position for OpenFileDialog/SaveFileDialog设置 OpenFileDialog/SaveFileDialog 的开始位置
【发布时间】:2009-08-10 17:25:10
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: c# winforms openfiledialog multiple-monitors


    【解决方案1】:

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

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

    【讨论】:

    • 这听起来很简单,它必须工作(至少它必须经过测试)!唉,在这个测试用例中,0-arg 和 1-arg ShowDialog 都以同样的方式失败: 1. 运行应用程序。 2.调用新的OpenFileDialog().ShowDialog(this);对话框与应用程序出现在同一监视器上。 3. 关闭对话框。 4. 将应用程序窗口拖到不同的监视器上。 5. 调用新的 OpenFileDialog().ShowDialog(this); original 监视器上出现对话框。尽管我在第 5 步中使用了新的 OpenFileDialog,但主应用程序的原始位置仍然存在一些问题。
    • 我(最后 :-) 选择 Marc 的答案是最好的,因为我最近发现它确实适用于 Windows 7。我的机器是 WinXP,其中我概述了测试用例只是上面仍然失败。我决定用同样的问题尝试 Microsoft 论坛,并获得了适用于 WinXP 的解决方案——请参阅此线程 (social.msdn.microsoft.com/Forums/en-US/winforms/thread/…) 获取代码。
    【解决方案2】:

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

    【讨论】:

    • 如果不愿意使用DllImports 就足够了。
    【解决方案3】:

    我是这样做的:

    我要显示 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。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        在 CodeProject 上查看this article。摘录:

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

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

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

        它的优点是我们仍然 完全控制 附加到的控件 打开文件对话框窗口。这意味着我们 可以接收事件、调用方法和 对那些做任何我们想做的事 控制。

        【讨论】:

          【解决方案6】:

          在 MSDN 上有一个相当老的例子。

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

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

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

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

              这似乎是最简单的方法

              谢谢罗伯! :-)

              【讨论】:

                猜你喜欢
                • 2010-11-13
                • 1970-01-01
                • 2014-01-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-04-27
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多