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
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());
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));
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.
//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;
}
}
}
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! :-)
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
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
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.
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 :
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);
}