WPF OpenFileDialog avec le pattern MVVM?

Je viens de commencer à apprendre le pattern MVVM pour WPF. Je frappe un mur: que faites-vous quand vous avez besoin de montrer un OpenFileDialog ?

Voici un exemple d’interface utilisateur que j’essaie d’utiliser sur:

texte alt

Lorsque le bouton Parcourir est cliqué, un fichier OpenFileDialog doit être affiché. Lorsque l’utilisateur sélectionne un fichier dans OpenFileDialog, le chemin du fichier doit être affiché dans la zone de texte.

Comment puis-je le faire avec MVVM?

Mise à jour : Comment puis-je le faire avec MVVM et le rendre testable? La solution ci-dessous ne fonctionne pas pour les tests unitaires.

Ce que je fais généralement, c’est créer une interface pour un service d’application qui exécute cette fonction. Dans mes exemples, je suppose que vous utilisez quelque chose comme MVVM Toolkit ou une chose similaire (pour obtenir un ViewModel de base et un RelayCommand).

Voici un exemple d’une interface extrêmement simple pour effectuer des opérations IO de base comme OpenFileDialog et OpenFile. Je les montre tous les deux ici, donc vous ne pensez pas que je vous suggère de créer une interface avec une méthode pour contourner ce problème.

public interface IOService { ssortingng OpenFileDialog(ssortingng defaultPath); //Other similar untestable IO operations Stream OpenFile(ssortingng path); } 

Dans votre application, vous fourniriez une implémentation par défaut de ce service. Voici comment vous allez le consumr.

 public MyViewModel : ViewModel { private ssortingng _selectedPath; public ssortingng 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 = ssortingng.Empty; } } } 

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

 [Test] public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty() { Mock ioServiceStub = new Mock(); //We use null to indicate invalid path in our implementation ioServiceStub.Setup(ioServ => ioServ.OpenFileDialog(It.IsAny())) .Returns(null); //Setup target and test MyViewModel target = new MyViewModel(ioServiceStub.Object); target.OpenCommand.Execute(); Assert.IsEqual(ssortingng.Empty, target.SelectedPath); } 

Cela fonctionnera probablement pour vous.

Il existe une bibliothèque sur CodePlex appelée “SystemWrapper” ( http://systemwrapper.codeplex.com ) qui pourrait vous éviter de devoir faire beaucoup de ce genre de choses. Il semble que FileDialog ne soit pas encore supporté, vous devrez donc écrire une interface pour celui-ci.

J’espère que cela t’aides.

Modifier :

Je semble me souvenir de votre préférence pour TypeMock Isolator pour votre cadre factice. Voici le même test avec Isolator:

 [Test] [Isolated] public void OpenFileCommand_UserSelectsInvalidPath_SelectedPathSetToEmpty() { IOService ioServiceStub = Isolate.Fake.Instance(); //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(ssortingng.Empty, target.SelectedPath); } 

J’espère que c’est utile aussi.

WPF Application Framework (WAF) fournit une implémentation pour Open et SaveFileDialog.

L’exemple d’application Writer montre comment les utiliser et comment le code peut être testé par unité.

Tout d’abord, je vous recommande de commencer avec un toolkit WPF MVVM . Cela vous donne une belle sélection de commandes à utiliser pour vos projets. Une caractéristique particulière qui a été rendue célèbre depuis l’introduction du pattern MVVM est le RelayCommand (il ya bien sûr d’autres versions, mais je m’en tiens aux plus courantes). C’est une implémentation de l’interface ICommand qui vous permet de créer une nouvelle commande dans votre ViewModel.

Retour à votre question, voici un exemple de ce que peut ressembler votre ViewModel.

 public class OpenFileDialogVM : ViewModelBase { public static RelayCommand OpenCommand { get; set; } private ssortingng _selectedPath; public ssortingng SelectedPath { get { return _selectedPath; } set { _selectedPath = value; RaisePropertyChanged("SelectedPath"); } } private ssortingng _defaultPath; public OpenFileDialogVM() { RegisterCommands(); } public OpenFileDialogVM(ssortingng 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 proviennent tous deux du MVVM Toolkit . Voici à quoi peut ressembler le XAML.

   

et votre code XAML.CS derrière.

 DataContext = new OpenFileDialogVM(); InitializeComponent(); 

C’est tout.

À mesure que vous vous familiarisez avec les commandes, vous pouvez également définir les conditions de désactivation du bouton Parcourir, etc. J’espère que cela vous a orienté dans la direction souhaitée.

À mon avis, la meilleure solution consiste à créer un contrôle personnalisé.

Le contrôle personnalisé que je crée habituellement est composé de:

  • Zone de texte ou bloc de texte
  • Bouton avec une image en tant que modèle
  • Propriété de dépendance de chaîne où le chemin du fichier sera encapsulé dans

Donc, le fichier * .xaml serait comme ça

         

Et le fichier * .cs:

  public static readonly DependencyProperty TextProperty = DependencyProperty.Register( "Text", typeof(ssortingng), typeof(customFilePicker), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal)); public ssortingng Text { get { return this.GetValue(TextProperty) as Ssortingng; } 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; } } 

À la fin, vous pouvez le lier à votre modèle de vue:

  

De mon sharepoint vue, la meilleure option est la bibliothèque de prisme et InteractionRequests. L’action pour ouvrir la boîte de dialog rest dans xaml et est déclenchée à partir de Viewmodel, alors que Viewmodel n’a pas besoin de connaître la vue.

Voir également

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

Comme exemple voir:

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