带有MVVM模式的WPF OpenFileDialog?

我刚开始学习WPF的MVVM模式。 我碰壁了: 当你需要显示一个OpenFileDialog时,你会做什么

以下是我尝试使用它的示例UI:

当浏览按钮被点击时,应该显示一个OpenFileDialog。 当用户从OpenFileDialog中选择一个文件时,文件路径应显示在文本框中。

我如何用MVVM做到这一点?

更新 :我如何使用MVVM做到这一点,并使其可以进行单元测试? 下面的解决方案不适用于单元测试。


我通常所做的是为执行此功能的应用程序服务创建一个接口。 在我的例子中,我假设你正在使用类似MVVM Toolkit或类似的东西(这样我可以得到一个基本的ViewModel和一个RelayCommand)。

下面是一个非常简单的界面,用于执行基本IO操作,如OpenFileDialog和OpenFile。 我在这里向他们展示,所以你不认为我建议你用一种方法创建一个接口来解决这个问题。

public interface IOService
{
     string OpenFileDialog(string defaultPath);

     //Other similar untestable IO operations
     Stream OpenFile(string path);
}

在您的应用程序中,您将提供此服务的默认实现。 这是你将如何消耗它。

public MyViewModel : ViewModel
{
     private string _selectedPath;
     public string SelectedPath
     {
          get { return _selectedPath; }
          set { _selectedPath = value; OnPropertyChanged("SelectedPath"); }
     }

     private RelayCommand _openCommand;
     public RelayCommand OpenCommand
     {
          //You know the drill.
          ...
     }

     private IOService _ioService;
     public MyViewModel(IOService ioService)
     {
          _ioService = ioService;
          OpenCommand = new RelayCommand(OpenFile);
     }

     private void OpenFile()
     {
          SelectedPath = _ioService.OpenFileDialog(@"c:WhereMyFileUsuallyIs.txt");
          if(SelectedPath == null)
          {
               SelectedPath = string.Empty;
          }
     }
}

所以这很简单。 现在是最后一部分:可测试性。 这一点应该很明显,但我会告诉你如何为此做一个简单的测试。 我使用Moq作为存根,但当然你可以使用任何你想要的。

[Test]
public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty()
{
     Mock<IOService> ioServiceStub = new Mock<IOService>();

     //We use null to indicate invalid path in our implementation
     ioServiceStub.Setup(ioServ => ioServ.OpenFileDialog(It.IsAny<string>()))
                  .Returns(null);

     //Setup target and test
     MyViewModel target = new MyViewModel(ioServiceStub.Object);
     target.OpenCommand.Execute();

     Assert.IsEqual(string.Empty, target.SelectedPath);
}

这可能适合你。

CodePlex上有一个名为“SystemWrapper”(http://systemwrapper.codeplex.com)的库,可以帮助您避免执行大量此类事情。 它看起来像FileDialog还不支持,所以你一定要为它写一个接口。

希望这可以帮助。

编辑

我似乎记得你偏爱伪装框架的TypeMock Isolator。 以下是使用隔离器的相同测试:

[Test]
[Isolated]
public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty()
{
    IOService ioServiceStub = Isolate.Fake.Instance<IOService>();

    //Setup stub arrangements
    Isolate.WhenCalled(() => ioServiceStub.OpenFileDialog("blah"))
           .WasCalledWithAnyArguments()
           .WillReturn(null);

     //Setup target and test
     MyViewModel target = new MyViewModel(ioServiceStub);
     target.OpenCommand.Execute();

     Assert.IsEqual(string.Empty, target.SelectedPath);
}

希望这也是有帮助的。


WPF应用程序框架(WAF)为Open和SaveFileDialog提供了一个实现。

Writer示例应用程序显示了如何使用它们以及如何对代码进行单元测试。


首先,我会建议您从WPF MVVM工具包开始。 这给你一个很好的用于你的项目的命令选择。 自从MVVM模式引入以来,一个特别的功能就是RelayCommand(当然还有其他版本,但我只是坚持使用最常用的)。 它是一个ICommand接口的实现,它允许你在你的ViewModel中创建一个新的命令。

回到你的问题,这里是你的ViewModel看起来像什么样子的例子。

public class OpenFileDialogVM : ViewModelBase
{
    public static RelayCommand OpenCommand { get; set; }
    private string _selectedPath;
    public string SelectedPath
    {
        get { return _selectedPath; }
        set
        {
            _selectedPath = value;
            RaisePropertyChanged("SelectedPath");
        }
    }

    private string _defaultPath;

    public OpenFileDialogVM()
    {
        RegisterCommands();
    }

    public OpenFileDialogVM(string defaultPath)
    {
        _defaultPath = defaultPath;
        RegisterCommands();
    }

    private void RegisterCommands()
    {
        OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
    }

    private void ExecuteOpenFileDialog()
    {
        var dialog = new OpenFileDialog { InitialDirectory = _defaultPath };
        dialog.ShowDialog();

        SelectedPath = dialog.FileName;
    }
}

ViewModelBaseRelayCommand都来自MVVM Toolkit。 这是XAML的外观。

<TextBox Text="{Binding SelectedPath}" />
<Button Command="vm:OpenFileDialogVM.OpenCommand" >Browse</Button>

和你的XAML.CS代码。

DataContext = new OpenFileDialogVM();
InitializeComponent();

而已。

随着您对这些命令的熟悉程度越来越高,您还可以根据您希望禁用“浏览”按钮等条件来设置条件。我希望能够按照您希望的方向指出您的位置。

链接地址: http://www.djcxy.com/p/20275.html

上一篇: WPF OpenFileDialog with the MVVM pattern?

下一篇: mvvm