WPF打开文件对话框与MVVM模式?(WPF OpenFileDialog with the MVV

2019-09-01 01:09发布

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

下面是我想要使用它的一个例子UI:

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

我如何与MVVM做到这一点?

更新 :我怎样才能做到这一点与MVVM并使其单元测试,能够? 该解决方案下面没有单元测试工作。

Answer 1:

我通常做的是创建一个执行此功能的应用服务的接口。 在我的例子中我假设你正在使用类似的工具包MVVM或类似的东西(这样我就可以得到基本视图模型和RelayCommand)。

这里有一个非常简单的界面,做这样的OpenFileDialog和基本的OpenFile IO操作的一个例子。 我展示他们都在这里,所以你不要以为我建议你创建一个接口与一个方法来解决这个问题。

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:\Where\My\File\Usually\Is.txt");
          if(SelectedPath == null)
          {
               SelectedPath = string.Empty;
          }
     }
}

所以这是非常简单的。 现在到了最后一部分:可测试性。 这应该是显而易见的,但我会告诉你如何做一个简单的测试此。 我用的起订量为磕碰,但你可以使用任何你想当然。

[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);
}

这可能会为你工作。

有一个称为“SystemWrapper”(CodePlex上库出http://systemwrapper.codeplex.com )可能挽救你不必做了很多这种东西的。 它看起来像目前还不支持FileDialog的,所以你一定得写一个接口。

希望这可以帮助。

编辑

我似乎记得你赞成TypeMock隔离您作假框架。 下面是使用隔离器相同的测试:

[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);
}

希望这是有帮助。



Answer 2:

WPF应用程序框架(WAF)提供了开放式和SaveFileDialog的实现。

作家示例应用程序显示如何使用它们,以及如何可以将代码单元进行测试。



Answer 3:

首先,我建议你用开始WPF MVVM工具包 。 这给你的命令不错的选择使用为您的项目。 因为MVVM模式的介绍已经提出了著名的特殊功能之一是RelayCommand(当然还有其他曼尼版本,但我只是坚持最常用的)。 其ICommand接口,让您箱子一个新的命令在您的视图模型的实现。

回到你的问题,这里是你的视图模型可能是什么样子的例子。

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工具包 。 这里是XAML可能是什么样子。

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

和你的XAML.CS后面的代码。

DataContext = new OpenFileDialogVM();
InitializeComponent();

而已。

当你更熟悉的命令,你也可以在需要时浏览按钮被禁用等等。我希望指出你在你想要的方向设定的条件为。



Answer 4:

从我的角度来看,最好的选择是棱镜图书馆和InteractionRequests。 打开对话框中的作用仍然是XAML内会从视图模型触发而视图模型并不需要了解该视图什么。

也可以看看

https://plainionist.github.io///Mvvm-Dialogs/

为例,请参阅:

https://github.com/plainionist/Plainion.Prism/blob/master/src/Plainion.Prism/Interactivity/PopupCommonDialogAction.cs

https://github.com/plainionist/Plainion.Prism/blob/master/src/Plainion.Prism/Interactivity/InteractionRequest/OpenFileDialogNotification.cs



Answer 5:

在我看来,最好的解决方法是创建一个自定义的控制。

自定义控制我通常创建由组成:

  • 文本框或文本块
  • 按键采用的图像作为模板
  • 字符串依赖属性所在的文件路径将会被包裹

所以*的.xaml文件会是这样

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Column="0" Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
    <Button Grid.Column="1" Click="Button_Click">
        <Button.Template>
            <ControlTemplate>
                <Image Grid.Column="1" Source="../Images/carpeta.png"/>
            </ControlTemplate>                
        </Button.Template>
    </Button>        
</Grid>

而* .cs文件:

public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
        typeof(string),
        typeof(customFilePicker),
        new FrameworkPropertyMetadata(null,
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal));

public string Text
{
    get
    {
        return this.GetValue(TextProperty) as String;
    }
    set
    {
        this.SetValue(TextProperty, value);
    }
}

public FilePicker()
{
    InitializeComponent();
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog openFileDialog = new OpenFileDialog();

    if(openFileDialog.ShowDialog() == true)
    {
        this.Text = openFileDialog.FileName;
    }
}

在结束时,你可以将它绑定到您的视图模型:

<controls:customFilePicker Text="{Binding Text}"/>


文章来源: WPF OpenFileDialog with the MVVM pattern?