2012-09-05 90 views
5

我试图从我的WPF应用程序中使用FolderBrowserDialog - 没有什么奇特的。我并不在意它有Windows窗体的外观。如何使用MVVM的WPF应用程序的FolderBrowserDialog

我找到了一个合适的答案(How to use a FolderBrowserDialog from a WPF application),除了我正在使用MVVM。

This是我“执行”的答案,除了我不能获得窗口对象,我只是打电话ShowDialog()没有任何参数。

问题是这样的:

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

在我ViewModel那里this对我来说得到窗口上下文中没有GetIWin32Window()方法。

关于如何使这项工作的任何想法?

回答

4

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

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

其次,您可以将应用程序的主窗口作为拥有窗口发送。

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

第二个选项可能不被认为是MVVMish。

请参阅@Dr. ABTthis question的回答,以便将视图的指针插入到您的ViewModel中(不确定这是一个好主意还是坏主意,但我不会让这阻止我)使用这种技术,如果您真的想使该视图成为FolderBrowserDialog的所有者,您将可以在虚拟机中访问相应的视图。

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

然后我们让VM /命令我们要使用的FolderBrowser进口IFolderBrowserDialog。在应用程序中,IFolderBrowserDialog.ShowDialog显示对话框。在单元测试中,我们模拟IFolderBrowserDialog,这样我们就可以验证它是否被调用了正确的参数,并且/或者将选定的文件夹发回到sut以便测试可以继续。

0

MVVM方式:

定义的FolderBrowserDialog一个新的接口。创建一个新类&实现该接口。 (实现与实际的FolderBrowserDialog类完成)。

这样你就不会将MVVM绑定到特定的实现,而实际的逻辑可以在以后测试。

+0

他仍然需要一个IWin32Window传递到的FolderBrowserDialog为它显示为一个适当的模式对话框。他想知道如何对ViewModel进行编程,以便能够检索当前视图的IWin32Window。 –

+0

是的,那是实施细节。就像我说的,如果你实现接口,实现可以做任何想做的事情。 Viewmodel不需要知道任何东西。您可以通过Application.Current.Windows.Where获取当前视图(x => x.IsActive = true) –

2

如果您决定使用FolderBrowserDialog,我会使用这种设计。

首先,在视图上创建一个DependencyProperty来暴露它的句柄。现在

public static readonly DependencyProperty WindowHandleProperty = 
    DependencyProperty.Register("WindowHandle", typeof(System.Windows.Forms.IWin32Window), typeof(MainWindow), new PropertyMetadata(null)); 

// MainWindow.cs 
public System.Windows.Forms.IWin32Window WindowHandle 
{ 
    get { return (System.Windows.Forms.IWin32Window)GetValue(WindowHandleProperty); } 
    set { SetValue(WindowHandleProperty, value); } 
} 

,当你的窗口负载,您可以使用问题提供的扩展检索手柄您链接到:

// MainWindow.cs 
void MainWindow_Loaded(object sender, RoutedEventArgs e) 
{ 
    var binding = new Binding(); 
    binding.Path = new PropertyPath("WindowHandle"); 
    binding.Mode = BindingMode.OneWayToSource; 
    SetBinding(WindowHandleProperty, binding); 

    WindowHandle = this.GetIWin32Window(); 
} 

所以,你是单向使用“绑定源WindowHandle“属性。所以,如果您的视图模型有WindowHandle属性,它会不断更新与相关视图的有效IWin32Handle:

// ViewModel.cs 
private System.Windows.Forms.IWin32Window _windowHandle; 
public System.Windows.Forms.IWin32Window WindowHandle 
{ 
    get 
    { 
     return _windowHandle; 
    } 
    set 
    { 
     if (_windowHandle != value) 
     { 
      _windowHandle = value; 
      RaisePropertyChanged("WindowHandle"); 
     } 
    } 
} 

这是一个很好的解决方案,因为你不硬编码一个视图模型中进行配对与一个特定的视图。如果你使用同一个ViewModel使用多个Views,它应该可以工作。如果你创建了一个新的视图,但你没有实现DependencyProperty,它将只用一个空的句柄来操作。

编辑:

作为一个侧面说明,你实际测试只是没有提供IWin32Owner参数?对我而言,它仍然会自动打开,作为应用程序的模式对话框,并阻止用户与应用程序的所有窗口进行交互。有什么你需要它来做呢?

0

要处理mvvm模式中的任何类型的对话框内容,您应该使用一种Dialog-Service。在this post中,你会发现一些提示符合这种方法。

将对话内容放入服务中可保持mvvm模式不变。该服务负责所有对话框的创建并提供结果。视图模型只是调用方法并订阅服务提供的事件。

如果您对服务(接口)使用依赖注入,那么您可以通过模拟来获得解决方案可测试的优势。或者您可以替换窗体folderbrowserdialog如果将有一个wpf之一。

0

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

相关问题