web-dev-qa-db-ja.com

OpenFileDialog / SaveFileDialogの開始位置を設定する

WinFormアプリケーションのカスタムダイアログ(フォーム)の場合、次のコマンドで表示する前に、サイズと位置を設定できます。

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

これは、複数のモニターを扱う場合に特に重要です。このようなコードがないと、2番目のモニターにドラッグしたアプリケーションからダイアログを開くと、ダイアログがプライマリモニターに表示されます。これにより、ユーザーエクスペリエンスが低下します。

標準の.NETOpenFileDialogおよびSaveFileDialog(StartPositionプロパティがない)の位置を設定するためのフックがあるかどうか疑問に思っています。

23
Michael Sorens

親として使用するためにIWin32Windowを受け入れる ShowDialog のオーバーロードを使用することが最善の方法だと思います。このmightは、適切な場所を選択するのに役立ちます。最も一般的に:

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

CodeProjectで この記事 をチェックしてください。抜粋:

便利な.NETNativeWindowが登場したとき、NativeWindowは、関連付けられたハンドルによって送信されたメッセージを処理するウィンドウラッパーです。 NativeWindowを作成し、OpenFileWindowハンドルをそれに関連付けます。この時点から、OpenFileWindowに送信されたすべてのメッセージは、代わりにNativeWindowの独自のWndProcメソッドにリダイレクトされ、キャンセル、変更、または通過させることができます。

WndProcでは、メッセージWM_WINDOWPOSCHANGINGを処理します。開いているダイアログが開いている場合は、ユーザーが設定したStartLocationに応じて、元の水平または垂直のサイズを変更します。作成するウィンドウのサイズが増加します。これは、コントロールが開かれたときに1回だけ発生します。

また、メッセージWM_SHOWWINDOWを処理します。ここでは、元のOpenFileDialog内のすべてのコントロールが作成され、ファイルを開くダイアログにコントロールを追加します。これは、Win32 APISetParentを呼び出すことによって行われます。このAPIを使用すると、親ウィンドウを変更できます。次に、基本的には、StartLocationプロパティの値に応じて、設定した場所にある元のOpenFileDialogにコントロールをアタッチします。

これの利点は、OpenFileDialogウィンドウにアタッチされたコントロールを完全に制御できることです。これは、イベントを受信し、メソッドを呼び出し、それらのコントロールで必要なことをすべて実行できることを意味します。

4
Charlie

私は昨日のほとんどの間この問題を抱えていました。 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
Iwan Morgan

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

これが私がそれをした方法です:

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
user20493

MSDNには1つのアプローチのかなり古い例があります。

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

これには、拡張性を可能にする独自のOpenFileDialogクラスを実装するために必要なすべてのコードが含まれています。

0
andynormancx

これに対するBobBの返事にとても感謝しています。さらにいくつかの「落とし穴」があります。 OpenFileDialog1.ShowDialog(PositionForm)を呼び出すときは、PositionFormのハンドルを渡す必要があります。そうしないと、BobBの手法がすべての場合に信頼できるとは限りません。また、W8.1がSkyDriveを含む新しいfileopenコントロールを起動するようになったため、W8.1fileopenコントロールのDocumentsフォルダーの場所がねじ込まれました。そこで、ShowHelp = Trueを設定して、fileopenを冷蔵庫に入れて古い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
0
Rob Sherratt