Для любого настраиваемого диалогового окна (формы) в приложении WinForm я могу установить его размер и положение, прежде чем отображать его с помощью:

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

Это особенно важно при работе с несколькими мониторами. Без такого кода, когда вы открываете диалоговое окно из приложения, которое вы перетащили на второй монитор, диалоговое окно появляется на основном мониторе. Это представляет собой плохой пользовательский опыт.

Мне интересно, есть ли какие-нибудь перехватчики для установки позиции для стандартных .NET OpenFileDialog и SaveFileDialog (у которых нет свойства StartPosition).

24
Michael Sorens 10 Авг 2009 в 21:25

8 ответов

Лучший ответ

Я подозреваю, что лучшее, что вы можете сделать, это использовать перегрузку ShowDialog, который принимает IWin32Window для использования в качестве родительского. Это может помочь ему выбрать подходящее место; Наиболее часто:

using(var dlg = new OpenFileDialog()) {
    .... setup
    if(dlg.ShowDialog(this) == DialogResult.OK) {
        .... use
    }
}
4
Marc Gravell 10 Авг 2009 в 18:30

Прочтите эту статью на CodeProject. Отрывок:

Вот когда появляется удобный .NET NativeWindow, NativeWindow - это оболочка окна, в которой она обрабатывает сообщения, отправленные связанным с ним дескриптором. Он создает NativeWindow и связывает с ним дескриптор OpenFileWindow. С этого момента каждое сообщение, отправленное в OpenFileWindow, будет вместо этого перенаправлено на наш собственный метод WndProc в NativeWindow, и мы можем отменить, изменить или позволить им пройти.

В нашем WndProc мы обрабатываем сообщение WM_WINDOWPOSCHANGING. Если открывается открытый диалог, мы изменим исходный горизонтальный или вертикальный размер в зависимости от StartLocation, установленного пользователем. Это увеличит размер создаваемого окна. Это происходит только один раз при открытии элемента управления.

Также мы обработаем сообщение WM_SHOWWINDOW. Здесь создаются все элементы управления внутри исходного OpenFileDialog, и мы собираемся добавить наш элемент управления в диалоговое окно открытия файла. Это делается путем вызова Win32 API SetParent. Этот API позволяет вам изменить родительское окно. Затем в основном то, что он делает, - это присоединение нашего элемента управления к исходному OpenFileDialog в установленном им месте, в зависимости от значения свойства StartLocation.

Преимущество этого состоит в том, что у нас по-прежнему есть полный контроль над элементами управления, прикрепленными к окну OpenFileDialog. Это означает, что мы можем получать события, вызывать методы и делать с этими элементами все, что захотим.

4
Charlie 10 Авг 2009 в 18:44

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();
3
BobB 30 Июн 2013 в 13:33

У меня была эта проблема большую часть вчерашнего дня. Ответ 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;
}
2
trashr0x 16 Ноя 2016 в 11:57

Вот как я это сделал:

Точка, в которой я хочу отобразить 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 для параметров положения / размера.)

Клуджи.

1
IAbstract 19 Авг 2016 в 21:07

Очень благодарен BobB за ответ на этот вопрос. Есть еще несколько "подводных камней". Вы должны передать дескриптор PositionForm при вызове OpenFileDialog1.ShowDialog (PositionForm), иначе метод BobB не будет надежным во всех случаях. Кроме того, теперь, когда W8.1 запускает новый элемент управления открытием файлов со SkyDrive, расположение папки «Документы» в элементе управления открытием файлов W8.1 изменено. Поэтому я открываю файл, чтобы использовать старый элемент управления W7, установив ShowHelp = True.

Вот код 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
0
shytikov 27 Авг 2015 в 10:01

Вдохновленный ответом Роба Шеррита от 22 января 2014 года, я создал новый модуль и назвал его 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 (как в коде Роба Шеррета) или любое другое значение, которое пожелаете.

Это казалось самым простым подходом

Спасибо, Роб! :-)

0
Chris Raisin 14 Июн 2020 в 13:16

В MSDN есть довольно старый пример одного подхода.

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

Он включает весь код, необходимый для реализации вашего собственного класса OpenFileDialog, который обеспечивает расширяемость.

0
andynormancx 10 Авг 2009 в 18:52