2009-08-10 170 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; 
}