How to use a FolderBrowserDialog from a WPF application
Asked Answered
W

10

59

I'm trying to use the FolderBrowserDialog from my WPF application - nothing fancy. I don't much care that it has the Windows Forms look to it.

However, when I call ShowDialog, I want to pass the owner window which is an IWin32Window. How do I get this from my WPF control?

Actually, does it matter? If I run this code and use the ShowDialog overload with no parameters it works fine. Under what circumstances do I need to pass the owner window?

Thanks,

Craig

Whang answered 24/11, 2008 at 19:26 Comment(1)
Check out Sven Groot's fantastic Ookii.Dialogs for both WinForms and WPF, which give you modern "Vista" style folder and file dialogs.Ro
W
65

And here's my final version.

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

And to actually use it:

var dlg = new FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dlg.ShowDialog(this.GetIWin32Window());
Whang answered 24/11, 2008 at 20:53 Comment(5)
When I run this code I get a Win32Exception thrown whenever the dialog closes. It doesn't seem to be a problem, and if I just catch it everything seems to work. Do you happen to know why that might be thrown?Contributory
Seems to work fine but I can't see any point to it myself, calling with 0 arguments still shows the 'same' modal dialog.Aquino
Worked beautifully for me, with no exceptions so far (.Net 3.5). Had to add a using statement for System.Windows.Interop. Easy to extend the solution for use with System.Windows.Forms.OpenFileDialog. Fellow readers - don't forget to check the DialogResult to make sure the user didn't click on the Cancel button.Jumble
In my Button event, this worked : dlg.ShowDialog(this.GetIWin32Window(this));Merrythought
Would't even compile without Farrukh's suggestion... the usage code above is wrong in that there is no parameter supplied in the call to GetIWin32Window( ???? ).Inaudible
C
17

If you specify Owner, you will get a Modal dialog over the specified WPF window.

To get WinForms compatible Win32 window create a class implements IWin32Window like this

 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
}

And use an instance of this class at your WinForms

        IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle; // 'this' means WPF Window
        folderBrowserDialog.ShowDialog(new OldWindow(mainWindowPtr));
Cusack answered 24/11, 2008 at 19:30 Comment(2)
Thanks for this - it's almost right - I'll post an answer below.Whang
This was exactly right and the only thing here that worked for me. System.Windows.PresentationSource.FromVisual(visual) was returning null.Squeteague
P
5

I realize this is an old question, but here is an approach which might be slightly more elegant (and may or may not have been available before)...

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

    }

}

Then, from your WPF window, you can simply...

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

}

Actually, does it matter?

I'm not sure if it matters in this case, but generally, you should tell Windows what is your window hierarchy, so if a parent window is clicked while child window is modal, Windows can provide a visual (and possibly audible) clue to the user.

Also, it ensures the "right" window is on top when there are multiple modal windows (not that I'm advocating such UI design). I've seen UIs designed by a certain multi-billion dollar corporation (which shell remain unnamed), that hanged simply because one modal dialog got "stuck" underneath another, and user had no clue it was even there, let alone how to close it.

Paraphrast answered 9/5, 2016 at 8:38 Comment(4)
What using do you use for this.GetIWin32Window()?Graft
@Graft I'm not sure I understand the question. Are asking about a using directive?Paraphrast
Yes. My Visual Studio doesn't say what using I need to get for thisGraft
@Graft Look at the first code snippet - GetIWin32Window is defined there as an extension method, so it is automatically accessible from the second snippet without using (assuming both snippets are in the same project).Paraphrast
W
2

OK, figured it out now - thanks to Jobi whose answer was close, but not quite.

From a WPF application, here's my code that works:

First a helper class:

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
}

Then, to use this:

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

I'm sure I can wrap this up better, but basically it works. Yay! :-)

Whang answered 24/11, 2008 at 20:27 Comment(1)
Try the last two lines from my code instead of this 4 lines, I think it will work for you with our the FromVisual call.Cusack
F
2
//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;
        }
    }
}
Filthy answered 27/7, 2010 at 16:4 Comment(1)
Does this works with wpf? I am not able to make it work; Isn't this code working only in windows forms?Flapjack
C
1

VB.net translation

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
Chaddy answered 10/4, 2014 at 8:56 Comment(0)
H
1

Here is a simple method.

System.Windows.Forms.NativeWindow winForm; 

public MainWindow()
{
    winForm = new System.Windows.Forms.NativeWindow();
    winForm.AssignHandle(new WindowInteropHelper(this).Handle);
    ...
}

public showDialog()
{
   dlgFolderBrowser.ShowDialog(winForm);
}
Hippogriff answered 9/4, 2019 at 21:47 Comment(0)
M
0

The advantage of passing an owner handle is that the FolderBrowserDialog will not be modal to that window. This prevents the user from interacting with your main application window while the dialog is active.

Marchand answered 24/11, 2008 at 19:38 Comment(0)
A
0

You should be able to get an IWin32Window by by using PresentationSource.FromVisual and casting the result to HwndSource which implements IWin32Window.

Also in the comments here:

Automobile answered 24/11, 2008 at 19:42 Comment(0)
C
0

Why not using the built in WindowInteropHelper class (see namespace System.Windows.Interop). This class already impelements the IWin32Window ;)

So you can forget about the "OldWindow class" ... the usage stays the same

Crucial answered 8/7, 2009 at 14:0 Comment(1)
Perhaps it used to, but in .NET 4 it doesn't.Blacktop

© 2022 - 2024 — McMap. All rights reserved.