Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a FolderBrowserDialog from a WPF application with MVVM

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.

I found a question with a suitable answer (How to use a FolderBrowserDialog from a WPF application), except I'm using MVVM.

This was the answer I "implemented", except I can't get the window object and I'm just calling ShowDialog() without any parameters.

The problem is this:

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

In my ViewModel there the this has no GetIWin32Window() method for me to get the Window context.

Any ideas on how to make this work?

like image 391
David Work Avatar asked Sep 05 '12 20:09

David Work


1 Answers

First, you could use the ShowDialog signature that does not require a window.

var dlg = new FolderBrowserDialog();
DialogResult result = dlg.ShowDialog();

Second, you could send the main window of the Application as the owning window.

var dlg = new FolderBrowserDialog();
DialogResult result = dlg.ShowDialog(Application.Current.MainWindow.GetIWin32Window());

The second option might not be considered very MVVMish.

See the answer by @Dr. ABT in this question for a way to inject a pointer to your View into your ViewModel (not sure if this is a good idea or a bad idea, but I'm not going to let that stop me) With this technique, you would have access in your VM to the corresponding View if you really want to make that View be the owner of the FolderBrowserDialog.

@ChrisDD is right about defining an interface and wrapping FolderBrowserDialog. That is how we do it:

  public interface IFolderBrowserDialog
  {
    string Description { get; set; }
    Environment.SpecialFolder RootFolder { get; set; }
    string SelectedPath { get; set; }
    bool ShowNewFolderButton { get; set; }
    bool? ShowDialog();
    bool? ShowDialog(Window owner);
  }

  //Decorated for MEF injection
  [Export(typeof(IFolderBrowserDialog))]
  [PartCreationPolicy(CreationPolicy.NonShared)]
  internal class WindowsFormsFolderBrowserDialog : IFolderBrowserDialog
  {
    private string _description;
    private string _selectedPath;

    [ImportingConstructor]
    public WindowsFormsFolderBrowserDialog()
    {
      RootFolder = System.Environment.SpecialFolder.MyComputer;
      ShowNewFolderButton = false;
    }

    #region IFolderBrowserDialog Members

    public string Description
    {
      get { return _description ?? string.Empty; }
      set { _description = value; }
    }

    public System.Environment.SpecialFolder RootFolder { get; set; }

    public string SelectedPath
    {
      get { return _selectedPath ?? string.Empty; }
      set { _selectedPath = value; }
    }

    public bool ShowNewFolderButton { get; set; }

    public bool? ShowDialog()
    {
      using (var dialog = CreateDialog())
      {
        var result = dialog.ShowDialog() == DialogResult.OK;
        if (result) SelectedPath = dialog.SelectedPath;
        return result;
      }
    }

    public bool? ShowDialog(Window owner)
    {
      using (var dialog = CreateDialog())
      {
        var result = dialog.ShowDialog(owner.AsWin32Window()) == DialogResult.OK;
        if (result) SelectedPath = dialog.SelectedPath;
        return result;
      }
    }
    #endregion

    private FolderBrowserDialog CreateDialog()
    {
      var dialog = new FolderBrowserDialog();
      dialog.Description = Description;
      dialog.RootFolder = RootFolder;
      dialog.SelectedPath = SelectedPath;
      dialog.ShowNewFolderButton = ShowNewFolderButton;
      return dialog;
    }
  }

  internal static class WindowExtensions
  {
    public static System.Windows.Forms.IWin32Window AsWin32Window(this Window window)
    {
      return new Wpf32Window(window);
    }
  }

  internal class Wpf32Window : System.Windows.Forms.IWin32Window
  {
    public Wpf32Window(Window window)
    {
      Handle = new WindowInteropHelper(window).Handle;
    }

    #region IWin32Window Members

    public IntPtr Handle { get; private set; }

    #endregion
  }

Then we make the VM/Command where we want to use the FolderBrowser import IFolderBrowserDialog. In application, IFolderBrowserDialog.ShowDialog shows the dialog. In unit test, we mock IFolderBrowserDialog so we can verify that it was called with correct parameters and/or send the selected folder back to the sut so that the test can continue.

like image 95
wageoghe Avatar answered Sep 25 '22 08:09

wageoghe