WPF OpenFileDialog с шаблоном MVVM?

Я только начал изучать шаблон MVVM для WPF. Я ударил стену: что вы делаете, когда вам нужно показать OpenFileDialog ?

Вот пример пользовательского интерфейса, на котором я пытаюсь использовать его:

alt text

При нажатии кнопки обзора открывается OpenFileDialog. Когда пользователь выбирает файл из OpenFileDialog, путь к файлу должен отображаться в текстовом поле.

Как я могу сделать это с помощью MVVM?

Обновление : как я могу сделать это с помощью MVVM и сделать его модульным? Нижеприведенное решение не работает для модульного тестирования.

Обычно я создаю интерфейс для службы приложений, которая выполняет эту функцию. В моих примерах я предполагаю, что вы используете что-то вроде MVVM Toolkit или аналогичной вещи (так что я могу получить базу ViewModel и RelayCommand).

Вот пример чрезвычайно простого интерфейса для выполнения основных операций ввода-вывода, таких как 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:\Where\My\File\Usually\Is.txt"); if(SelectedPath == null) { SelectedPath = string.Empty; } } } 

Так что это довольно просто. Теперь для последней части: тестируемость. Это должно быть очевидно, но я покажу вам, как сделать простой тест для этого. Я использую Moq для stubbing, но вы можете использовать все, что захотите, конечно.

 [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(string.Empty, target.SelectedPath); } 

Это, вероятно, сработает для вас.

В CodePlex есть библиотека, называемая «SystemWrapper» ( http://systemwrapper.codeplex.com ), которая может избавить вас от необходимости делать много такого. Похоже, FileDialog еще не поддерживается, поэтому вам обязательно нужно написать интерфейс для этого.

Надеюсь это поможет.

Изменить :

Кажется, я помню, как вы предпочитаете TypeMock Isolator для вашей фальсификации. Вот те же тесты с использованием 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(string.Empty, target.SelectedPath); } 

Надеюсь, это тоже полезно.

WPF Application Framework (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; } } 

ViewModelBase и RelayCommand являются как из MVVM Toolkit . Вот что может выглядеть XAML.

   

и ваш код XAML.CS позади.

 DataContext = new OpenFileDialogVM(); InitializeComponent(); 

Это оно.

Познакомившись с командами, вы также можете установить условия, когда вы хотите, чтобы кнопка «Обзор» была отключена, и т. Д. Я надеюсь, что указал на вас в том направлении, в котором вы хотели.

На мой взгляд, лучшим решением является создание настраиваемого элемента управления.

Пользовательский элемент управления, который я обычно создаю, состоит из:

  • Текстовое поле или текстовый блок
  • Кнопка с изображением в качестве шаблона
  • Свойство String dependency, где путь к файлу будет завернут в

Таким образом, файл * .xaml будет таким образом

         

И файл * .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; } } 

В конце вы можете привязать его к своей модели просмотра:

  

С моей точки зрения лучшим вариантом является библиотека призмы и InteractionRequests. Действие для открытия диалогового windows остается в пределах xaml и запускается из Viewmodel, в то время как Viewmodel не должен ничего знать о представлении.

Смотрите также

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

  • Проблема с разрешением экрана в WPF?
  • Загрузка XAML во время выполнения?
  • BitmapImage to byte
  • Проблемы с локализацией строки StringFormat в wpf
  • Как создать DataTemplate в коде c #?
  • как можно включить полосы прокрутки в WPF Datagrid?
  • Как добавить поведение Blend в стильном сеттере
  • Неправильно ли использовать Диспетчер в моей модели ViewModel?
  • Связывание со статическим classом
  • Пользовательский курсор в WPF?
  • Как вы выполняете эффекты перехода с помощью элемента управления Frame в WPF?
  • Давайте будем гением компьютера.