Cómo usar un FolderBrowserDialog desde una aplicación WPF
winapi (9)
Intento usar el FolderBrowserDialog desde mi aplicación WPF, nada lujoso. No me importa mucho que tenga el aspecto de Windows Forms.
Sin embargo, cuando llamo a ShowDialog, quiero pasar la ventana del propietario que es un IWin32Window. ¿Cómo obtengo esto de mi control WPF?
En realidad, ¿Importa? Si ejecuto este código y uso la sobrecarga ShowDialog sin parámetros, funciona bien. ¿Bajo qué circunstancias necesito pasar la ventana del propietario?
Gracias,
Craig
¿Por qué no utilizar la clase integrada WindowInteropHelper (ver espacio de nombres System.Windows.Interop). Esta clase ya impele el IWin32Window;)
Así que puedes olvidarte de la "clase OldWindow" ... el uso permanece igual
Debería poder obtener un IWin32Window utilizando PresentationSource.FromVisual y emitiendo el resultado a HwndSource que implementa IWin32Window.
También en los comentarios here :
La ventaja de pasar un identificador de propietario es que el FolderBrowserDialog no será modal para esa ventana. Esto evita que el usuario interactúe con la ventana principal de la aplicación mientras el cuadro de diálogo está activo.
Me doy cuenta de que esta es una vieja pregunta, pero este es un enfoque que podría ser un poco más elegante (y puede o no haber estado disponible antes) ...
using System;
using System.Windows;
using System.Windows.Forms;
// ...
/// <summary>
/// Utilities for easier integration with WinForms.
/// </summary>
public static class WinFormsCompatibility {
/// <summary>
/// Gets a handle of the given <paramref name="window"/> and wraps it into <see cref="IWin32Window"/>,
/// so it can be consumed by WinForms code, such as <see cref="FolderBrowserDialog"/>.
/// </summary>
/// <param name="window">
/// The WPF window whose handle to get.
/// </param>
/// <returns>
/// The handle of <paramref name="window"/> is returned as <see cref="IWin32Window.Handle"/>.
/// </returns>
public static IWin32Window GetIWin32Window(this Window window) {
return new Win32Window(new System.Windows.Interop.WindowInteropHelper(window).Handle);
}
/// <summary>
/// Implementation detail of <see cref="GetIWin32Window"/>.
/// </summary>
class Win32Window : IWin32Window { // NOTE: This is System.Windows.Forms.IWin32Window, not System.Windows.Interop.IWin32Window!
public Win32Window(IntPtr handle) {
Handle = handle; // C# 6 "read-only" automatic property.
}
public IntPtr Handle { get; }
}
}
Luego, desde su ventana de WPF, puede simplemente ...
public partial class MainWindow : Window {
void Button_Click(object sender, RoutedEventArgs e) {
using (var dialog = new FolderBrowserDialog()) {
if (dialog.ShowDialog(this.GetIWin32Window()) == System.Windows.Forms.DialogResult.OK) {
// Use dialog.SelectedPath.
}
}
}
}
En realidad, ¿Importa?
No estoy seguro de si importa en este caso , pero en general, debe decirle a Windows cuál es su jerarquía de ventanas, de modo que si se hace clic en una ventana principal mientras que la ventana secundaria es modal, Windows puede proporcionar una pista visual (y posiblemente audible) el usuario.
Además, garantiza que la ventana "derecha" esté en la parte superior cuando hay varias ventanas modales (no es que defienda ese diseño de IU). He visto UI diseñadas por una determinada corporación multimillonaria (cuyo shell no se menciona), que se colgaron simplemente porque un diálogo modal se "atascó" debajo de otro, y el usuario no tenía ni idea de que estaba allí, y mucho menos de cómo cerrar eso.
OK, lo descubrí ahora, gracias a Jobi cuya respuesta fue cercana, pero no del todo.
Desde una aplicación WPF, aquí está mi código que funciona:
Primero una clase de ayudante:
private class OldWindow : System.Windows.Forms.IWin32Window
{
IntPtr _handle;
public OldWindow(IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
Entonces, para usar esto:
System.Windows.Forms.FolderBrowserDialog dlg = new FolderBrowserDialog();
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
System.Windows.Forms.IWin32Window win = new OldWindow(source.Handle);
System.Windows.Forms.DialogResult result = dlg.ShowDialog(win);
Estoy seguro de que puedo terminar esto mejor, pero básicamente funciona. ¡Hurra! :-)
Si especifica Propietario, obtendrá un diálogo modal sobre la ventana WPF especificada.
Para obtener WinForms compatible ventana Win32 crear una clase implementa IWin32Window como este
public class OldWindow : System.Windows.Forms.IWin32Window
{
IntPtr _handle;
public OldWindow(IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
Y use una instancia de esta clase en sus WinForms
IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle; // ''this'' means WPF Window
folderBrowserDialog.ShowDialog(new OldWindow(mainWindowPtr));
Traducción VB.net
Module MyWpfExtensions
Public Function GetIWin32Window(this As Object, visual As System.Windows.Media.Visual) As System.Windows.Forms.IWin32Window
Dim source As System.Windows.Interop.HwndSource = System.Windows.PresentationSource.FromVisual(Visual)
Dim win As System.Windows.Forms.IWin32Window = New OldWindow(source.Handle)
Return win
End Function
Private Class OldWindow
Implements System.Windows.Forms.IWin32Window
Public Sub New(handle As System.IntPtr)
_handle = handle
End Sub
Dim _handle As System.IntPtr
Public ReadOnly Property Handle As IntPtr Implements Forms.IWin32Window.Handle
Get
End Get
End Property
End Class
End Module
Y esta es mi versión final.
public static class MyWpfExtensions
{
public static System.Windows.Forms.IWin32Window GetIWin32Window(this System.Windows.Media.Visual visual)
{
var source = System.Windows.PresentationSource.FromVisual(visual) as System.Windows.Interop.HwndSource;
System.Windows.Forms.IWin32Window win = new OldWindow(source.Handle);
return win;
}
private class OldWindow : System.Windows.Forms.IWin32Window
{
private readonly System.IntPtr _handle;
public OldWindow(System.IntPtr handle)
{
_handle = handle;
}
#region IWin32Window Members
System.IntPtr System.Windows.Forms.IWin32Window.Handle
{
get { return _handle; }
}
#endregion
}
}
Y para usarlo realmente:
var dlg = new FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dlg.ShowDialog(this.GetIWin32Window());
//add a reference to System.Windows.Forms.dll
public partial class MainWindow : Window, System.Windows.Forms.IWin32Window
{
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
var fbd = new FolderBrowserDialog();
fbd.ShowDialog(this);
}
IntPtr System.Windows.Forms.IWin32Window.Handle
{
get
{
return ((HwndSource)PresentationSource.FromVisual(this)).Handle;
}
}
}