5

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?

4

6 に答える 6

4

まず、ウィンドウを必要としない ShowDialog シグネチャを使用できます。

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

次に、アプリケーションのメイン ウィンドウを所有ウィンドウとして送信できます。

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

2 番目のオプションは、あまり MVVMish とは見なされない可能性があります。

View へのポインターを ViewModel に挿入する方法については、この質問の@ Dr. ABTによる回答を参照してください (これが良いアイデアか悪いアイデアかはわかりませんが、それで私を止めるつもりはありません)。この手法を使用すると、そのビューを FolderBrowserDialog の所有者にしたい場合は、VM で対応するビューにアクセスできます。

@ChrisDD は、インターフェイスの定義と FolderBrowserDialog のラッピングについて正しいです。それが私たちのやり方です:

  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 に戻したりして、テストを続行できるようにします。

于 2012-09-05T20:46:50.920 に答える
1

動作としての MVVM + WinForms FolderBrowserDialog

public class FolderDialogBehavior : Behavior<Button>
{
    public string SetterName { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Click += OnClick;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Click -= OnClick;
    }

    private void OnClick(object sender, RoutedEventArgs e)
    {
        var dialog = new FolderBrowserDialog();
        var result = dialog.ShowDialog();
        if (result == DialogResult.OK && AssociatedObject.DataContext != null)
        {
            var propertyInfo = AssociatedObject.DataContext.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanRead && p.CanWrite)
            .Where(p => p.Name.Equals(SetterName))
            .First();

            propertyInfo.SetValue(AssociatedObject.DataContext, dialog.SelectedPath, null);
        }
    }
}

使用法

     <Button Grid.Column="3" Content="...">
            <Interactivity:Interaction.Behaviors>
                <Behavior:FolderDialogBehavior SetterName="SomeFolderPathPropertyName"/>
            </Interactivity:Interaction.Behaviors>
     </Button>

ブログ投稿: http://kostylizm.blogspot.ru/2014/03/wpf-mvvm-and-winforms-folder-dialog-how.html

于 2014-04-09T10:37:21.923 に答える
0

mvvmパターン内のあらゆる種類のダイアログを処理するには、一種のDialog-Serviceを使用する必要があります。この投稿では、このアプローチを使用するためのヒントをいくつか紹介します。

ダイアログのものをサービスに入れると、mvvmパターンはそのままになります。このサービスは、ダイアログのすべての作成を処理し、結果を提供できます。ビューモデルは、メソッドを呼び出し、サービスによって提供されるイベントをサブスクライブするだけです。

サービス(インターフェース)に依存性注入を使用する場合、モックによってソリューションをテスト可能に保つという利点があります。または、wpfが存在する場合は、formsfolderbrowserdialogを置き換えることができます。

于 2012-09-05T23:10:16.670 に答える
0

MVVM の方法:

FolderBrowserDialog の新しいインターフェイスを定義します。新しいクラスを作成し、そのインターフェイスを実装します。(実装は実際の FolderBrowserDialog クラスで行われます)。

この方法では、MVVM を特定の実装に関連付けず、実際のロジックを後でテストできます。

于 2012-09-05T20:50:30.810 に答える