2009-08-10 18 views
20

Para cualquier diálogo personalizado (forma) en una aplicación WinForm puedo fijar su tamaño y posición antes de mostrarlo con:Ajuste de la posición de inicio de OpenFileDialog/SaveFileDialog

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

Esto es particularmente importante cuando se trata de múltiples monitores. Sin dicho código, cuando abre un diálogo desde una aplicación que ha arrastrado a un segundo monitor, el diálogo aparece en el monitor principal. Esto presenta una experiencia de usuario pobre.

Me pregunto si hay ganchos para establecer la posición para .NET OpenFileDialog y SaveFileDialog (que no tienen una propiedad StartPosition).

Respuesta

3

Sospecho que lo mejor que puede hacer es asegurarse de usar el overload of ShowDialog que acepta un IWin32Window para usar como padre. Este podría ayudarlo a elegir una ubicación adecuada; más comúnmente:

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

Esto sonaba tan simple que tenía que funcionar (¡al menos había que probarlo!). Desgraciadamente, tanto el 0-arg como el 1-arg ShowDialog fallan de la misma manera en este caso de prueba: 1. Ejecute la aplicación. 2. Invoque el nuevo OpenFileDialog(). ShowDialog (this); el diálogo aparece en el mismo monitor que la aplicación. 3. Cerrar el cuadro de diálogo. 4. Arrastre la ventana de la aplicación a un monitor diferente. 5. Invoque el nuevo OpenFileDialog(). ShowDialog (this); aparece el cuadro de diálogo en el monitor * original *. Aunque uso un OpenFileDialog nuevo en el paso 5, todavía hay algo persistente sobre la ubicación original de la aplicación principal. –

+1

Estoy (finalmente :-) eligiendo la respuesta de Marc como la mejor porque descubrí que * no * se aplica a Windows 7. Mi máquina es WinXP, donde el caso de prueba que delineo arriba todavía falla. Decidí probar los foros de Microsoft con la misma pregunta y me dieron una solución que funciona para WinXP: mira este hilo (http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/dec48489- 0a57-4baa-b401-82266be782e0) para el código. –

4

Salida this article en CodeProject. Extracto:

Aquí es cuando el .NET útil NativeWindow entra en la imagen, un NativeWindow es un envoltorio ventana donde que procesa los mensajes enviados por el mango asociada a la misma. Crea una ventana nativa y asocia el manejador OpenFileWindow . Desde este punto , todos los mensajes enviados a OpenFileWindow será redirigido a nuestro propio método WndProc en el NativeWindow lugar, y podemos cancelar, modificar, o dejarlos pasar a través .

En nuestro WndProc, procesamos el mensaje WM_WINDOWPOSCHANGING. Si se abre el cuadro de diálogo abierto, cambiaremos el tamaño original horizontal o vertical dependiendo de la StartLocation establecida por el usuario. Incrementará el tamaño de la ventana que se creará. Este ocurre solo una vez cuando el control está abierto .

Además, procesaremos el mensaje WM_SHOWWINDOW. Aquí, todos los controles dentro del OpenFileDialog original son creados, y vamos a anexar nuestro control al diálogo de archivo abierto. Esto se hace llamando a una API Win32 SetParent. Esta API le permite cambiar la ventana principal. Entonces, básicamente lo que hace es conectar nuestro control al OpenFileDialog original en la ubicación que estableció, dependiendo del valor de la propiedad StartLocation.

La ventaja de ello es que todavía un control completo sobre los controles conectados a la ventana OpenFileDialog. Esto significa que puede recibir eventos, llamar a métodos y hacer lo que queramos con esos controles .

0

Así es como lo hice:

El punto en el que desea mostrar el OpenFileDialog:

Thread posThread = new Thread(positionOpenDialog); 
posThread.Start(); 

DialogResult dr = ofd.ShowDialog(); 

El código de reposicionamiento:

[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); 
} 

inicio un hilo que busca una ventana con el título "Abrir". (Normalmente se encuentra en 3 iteraciones o 15 milisegundos). Luego configuro su posición con el asa obtenida. (Consulte la documentación de SetWindowPos para conocer los parámetros de posición/tamaño).

Kludgy.

1

OpenFileDialog y SaveFileDialog se posicionan en la esquina superior izquierda de el área de cliente de la ventana que se muestra más recientemente. Así que solo crea una nueva ventana invisible ubicada donde quieras que aparezca el cuadro de diálogo antes de crear y mostrar ese cuadro de diálogo.

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

Adecuado si es reacio a utilizar 'DllImport's. – IAbstract

0

Muy agradecido por la respuesta de BobB en este caso. Hay algunos más "errores". Debe pasar el control de PositionForm al llamar a OpenFileDialog1.ShowDialog (PositionForm); de lo contrario, la técnica de BobB no es confiable en todos los casos. Además, ahora que W8.1 lanza un nuevo control de archivo abierto con SkyDrive, la ubicación de la carpeta Documentos en el control de archivos abiertos de W8.1 está ahora atornillada. Así que frig fileopen para usar el viejo control W7 configurando ShowHelp = True.

Aquí está el código de VB.NET que terminé usando, mi contribución a la comunidad en caso de que me ayude.

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

Tuve este problema durante la mayor parte de ayer. La respuesta de BobB fue la que más me ayudó (Gracias BobB).

Incluso puede llegar a crear un método privado que cree una ventana y la cierre antes de la llamada al método dialog.ShowDialog() y aún se centrará en 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; 
} 

Entonces llaman en cualquier método antes del método 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; 
} 
Cuestiones relacionadas