web-dev-qa-db-fra.com

Comment utiliser un FolderBrowserDialog à partir d'une application WPF

J'essaie d'utiliser le FolderBrowserDialog de mon application WPF - rien d'extraordinaire. Peu m'importe qu'il ait l'aspect Windows Forms.

Cependant, lorsque j'appelle ShowDialog, je veux passer la fenêtre propriétaire qui est un IWin32Window. Comment puis-je obtenir cela de mon contrôle WPF?

En fait, est-ce important? Si j'exécute ce code et utilise la surcharge ShowDialog sans paramètres, cela fonctionne très bien. Dans quelles circonstances dois-je passer la fenêtre du propriétaire?

Merci,

Craig

51
Craig Shearer

Et voici ma version finale.

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
    }
}

Et pour l'utiliser réellement:

var dlg = new FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dlg.ShowDialog(this.GetIWin32Window());
57
Craig Shearer

Si vous spécifiez Owner, vous obtiendrez une boîte de dialogue Modal sur la fenêtre WPF spécifiée.

Pour obtenir une fenêtre Win32 compatible WinForms, créez une classe implémentant IWin32Window comme ceci

 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
}

Et utilisez une instance de cette classe dans vos WinForms

        IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle; // 'this' means WPF Window
        folderBrowserDialog.ShowDialog(new OldWindow(mainWindowPtr));
16
Jobi Joy

Je me rends compte que c'est une vieille question, mais voici une approche qui pourrait être légèrement plus élégante (et qui peut ou non avoir été disponible auparavant) ...

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

    }

}

Ensuite, depuis votre fenêtre WPF, vous pouvez simplement ...

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 fait, est-ce important?

Je ne sais pas si cela importe dans ce cas, mais en général, vous devez dire à Windows quelle est votre hiérarchie de fenêtres, donc si une fenêtre parent est cliquée alors que la fenêtre enfant est modale, Windows peut fournir un visuel (et éventuellement audible) un indice pour l'utilisateur.

En outre, cela garantit que la "bonne" fenêtre est au-dessus lorsqu'il y a plusieurs fenêtres modales (pas que je préconise une telle conception d'interface utilisateur). J'ai vu des interfaces utilisateur conçues par une certaine société de plusieurs milliards de dollars (dont Shell reste sans nom), qui pendaient simplement parce qu'une boîte de dialogue modale s'est "coincée" sous une autre, et l'utilisateur n'avait aucune idée qu'il était même là, et encore moins comment fermer il.

3
//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;
        }
    }
}
2
Bruno

OK, compris maintenant - grâce à Jobi dont la réponse était proche, mais pas tout à fait.

À partir d'une application WPF, voici mon code qui fonctionne:

D'abord une classe d'aide:

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
}

Ensuite, pour utiliser ceci:

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

Je suis sûr que je peux mieux résumer cela, mais en gros cela fonctionne. Yay! :-)

2
Craig Shearer

Traduction 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
1
user2307482

Pourquoi ne pas utiliser la classe WindowInteropHelper intégrée (voir l'espace de noms System.Windows.Interop). Cette classe stimule déjà le IWin32Window;)

Vous pouvez donc oublier la "classe OldWindow" ... l'utilisation reste la même

0
Kenshin

L'avantage de passer un handle de propriétaire est que le FolderBrowserDialog ne sera pas modal à cette fenêtre. Cela empêche l'utilisateur d'interagir avec la fenêtre principale de votre application pendant que la boîte de dialogue est active.

0
Andy

Vous devriez pouvoir obtenir un IWin32Window en utilisant PresentationSource.FromVisual et en convertissant le résultat en HwndSource qui implémente IWin32Window.

Aussi dans les commentaires ici :

0
jageall

Voici une méthode simple.


System.Windows.Forms.NativeWindow winForm; 
public MainWindow()
{
    winForm = new System.Windows.Forms.NativeWindow();
    winForm.AssignHandle(new WindowInteropHelper(this).Handle);
    ...
}
public showDialog()
{
   dlgFolderBrowser.ShowDialog(winForm);
}
0
Shangwu