如何通过MVVM在WPF应用程序中使用FolderBrowserDialog

如何通过MVVM在WPF应用程序中使用FolderBrowserDialog

问题描述:

我正在尝试使用 FolderBrowserDialog -没什么.我不太在乎它具有Windows窗体外观.

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.

我找到了一个具有适当答案的问题(如何使用WPF应用程序中的FolderBrowserDialog ),但我使用的是MVVM.

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

是我实施"的答案,但无法获得窗口对象,而我只是不带任何参数调用 ShowDialog().

问题是这样的:

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

在我的 ViewModel 中, this 没有用于我获取Window上下文的 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?

首先,您可以使用不需要窗口的ShowDialog签名.

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

第二个选项可能不被视为非常MVVMish.

The second option might not be considered very MVVMish.

通过@ 博士查看答案.此问题中的ABT ,用于注入将您的View指向ViewModel的指针(不确定这是一个好主意还是一个坏主意,但我不会阻止我停止前进)使用这种技术,如果您可以在VM中访问相应的View我真的想让View成为FolderBrowserDialog的所有者.

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关于定义接口和包装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
  }

然后,在要使用FolderBrowser导入IFolderBrowserDialog的位置制作VM/命令.在应用程序中,IFolderBrowserDialog.ShowDialog显示对话框.在单元测试中,我们模拟了IFolderBrowserDialog,以便可以验证是否使用正确的参数调用了它,并且/或者将选定的文件夹发送回sut,以便测试可以继续.

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.