104 votes

WPF OpenFileDialog avec le modèle MVVM?

Je viens de commencer l'apprentissage de la pattern MVVM pour WPF. J'ai frappé un mur: que faites-vous quand vous avez besoin de montrer un OpenFileDialog?

Voici un exemple de l'INTERFACE utilisateur, je suis en train de l'utiliser sur:

alt text

Lorsque le bouton parcourir est cliqué, un OpenFileDialog doit être indiqué. Lorsque l'utilisateur sélectionne un fichier à partir de la OpenFileDialog, le chemin du fichier doit être affiché dans la zone de texte.

Comment puis-je faire cela avec MVVM?

Mise à jour: Comment puis-je faire cela avec MVVM et faire de test de l'unité-mesure? La solution ci-dessous ne fonctionne pas pour les tests unitaires.

104voto

Anderson Imes Points 18093

Ce que je fais généralement est de créer une interface pour une application de service qui exerce cette fonction. Dans mes exemples, je vais supposer que vous êtes en utilisant quelque chose comme le Toolkit MVVM ou chose semblable (si je peux obtenir une base ViewModel et un RelayCommand).

Voici un exemple d'une interface extrêmement simple pour faire des opérations d'e / s de base comme OpenFileDialog et OpenFile. Je suis les montrant tous les deux ici, tu ne pense pas que je suis en vous proposant de créer une interface avec une méthode pour contourner ce problème.

public interface IOService
{
     string OpenFileDialog(string defaultPath);

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

Dans votre demande, vous devez fournir une implémentation par défaut de ce service. Voici comment vous pourriez consommer.

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

Donc, c'est assez simple. Maintenant, pour la dernière partie: la testabilité. Celui-ci devrait être évident, mais je vais vous montrer comment faire un test simple pour cela. J'utilise Moq pour cogner, mais vous pouvez utiliser tout ce que vous souhaitez bien sûr.

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

Ce sera probablement pour vous.

Il y a une bibliothèque sur CodePlex appelé "SystemWrapper" (http://systemwrapper.codeplex.com) qui pourrait vous éviter d'avoir à faire beaucoup de ce genre de chose. Il ressemble à FileDialog n'est pas encore pris en charge, de sorte que vous aurez certainement à écrire une interface pour que l'on.

Espérons que cette aide.

Edit:

Il me semble me rappeler que vous favorisant TypeMock Isolateur pour votre semblant cadre. Voici le même test à l'aide de l'Isolateur:

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

Espérons que cela est utile.

6voto

jbe Points 4629

L' Application WPF Cadre (WAF) fournit une application pour l'Ouvrir et SaveFileDialog.

L'Écrivain exemple d'application montre comment les utiliser et comment le code peut être l'objet de tests unitaires.

4voto

Tri Q Points 2661

Tout d'abord, je vous recommande de commencer par un WPF toolkit MVVM. Cela vous donne une belle sélection de Commandes à utiliser pour vos projets. Une fonctionnalité particulière qui a été rendu célèbre depuis le pattern MVVM de l'introduction est la RelayCommand (il y a manny autres versions, bien sûr, mais je viens de coller le plus couramment utilisé). Ses une implémentation de l'interface ICommand qui vous permet de créer une nouvelle commande dans votre ViewModel.

Pour revenir à ta question,voici un exemple de ce que votre ViewModel peut ressembler.

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

ViewModelBase et RelayCommand sont à la fois de la Toolkit MVVM. Voici ce que le code XAML peut ressembler.

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

et votre XAML.CS code derrière.

DataContext = new OpenFileDialogVM();
InitializeComponent();

Thats it.

Comme vous familiariser avec les commandes, vous pouvez également définir des conditions pour quand vous voulez le bouton Parcourir pour être handicapés, etc. J'espère que de relever la direction voulu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X