with que programacion principiantes para conceptos codeproject basico wpf mvvm openfiledialog

que - WPF OpenFileDialog con el patrón MVVM?



wpf c# mvvm (5)

Desde mi perspectiva, la mejor opción es la biblioteca de prismas y las Solicitudes de interacción. La acción para abrir el diálogo permanece dentro del xaml y se activa desde Viewmodel, mientras que el modelo de vista no necesita saber nada sobre la vista.

Ver también

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

Como ejemplo, ver:

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

Acabo de empezar a aprender el patrón MVVM para WPF. Golpeé una pared: ¿qué haces cuando necesitas mostrar un OpenFileDialog ?

Aquí hay una interfaz de usuario de ejemplo en la que estoy tratando de usar:

Cuando se hace clic en el botón Examinar, se debe mostrar un OpenFileDialog. Cuando el usuario selecciona un archivo de OpenFileDialog, la ruta del archivo debe mostrarse en el cuadro de texto.

¿Cómo puedo hacer esto con MVVM?

Actualización : ¿Cómo puedo hacer esto con MVVM y convertirlo en prueba de la unidad? La solución a continuación no funciona para las pruebas unitarias.


El WPF Application Framework (WAF) proporciona una implementación para Open y SaveFileDialog.

La aplicación de ejemplo Writer muestra cómo usarlos y cómo el código puede ser probado en unidades.


En mi opinión, la mejor solución es crear un control personalizado.

El control personalizado que normalmente creo se compone de:

  • Textbox o textblock
  • Botón con una imagen como plantilla
  • Propiedad de cadena de dependencia donde la ruta del archivo se ajustará a

Entonces el archivo * .xaml sería así

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

Y el archivo * .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; } }

Al final puede vincularlo a su modelo de vista:

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


En primer lugar, recomendaría que comenzaras con un kit de herramientas WPF MVVM . Esto le ofrece una buena selección de Comandos para usar en sus proyectos. Una característica particular que se ha hecho famosa desde la introducción del patrón MVVM es el RelayCommand (hay otras versiones de Manny, por supuesto, pero me limito a las más utilizadas). Es una implementación de la interfaz de ICommand que le permite crear un nuevo comando en su ViewModel.

De vuelta a su pregunta, aquí hay un ejemplo de cómo se verá su 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 y RelayCommand son ambos de MVVM Toolkit . Aquí está lo que el XAML puede parecer.

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

y su código XAML.CS detrás.

DataContext = new OpenFileDialogVM(); InitializeComponent();

Eso es.

A medida que se familiarice con los comandos, también puede establecer las condiciones para cuando quiera que se deshabilite el botón Examinar, etc. Espero que lo haya apuntado en la dirección que deseaba.


Lo que generalmente hago es crear una interfaz para un servicio de aplicación que realiza esta función. En mis ejemplos, supongo que está utilizando algo como MVVM Toolkit o algo similar (para que pueda obtener un ViewModel base y un RelayCommand).

Aquí hay un ejemplo de una interfaz extremadamente simple para hacer operaciones básicas de IO como OpenFileDialog y OpenFile. Los mostraré aquí, así que no creo que sugiera que cree una interfaz con un método para evitar este problema.

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

En su aplicación, proporcionaría una implementación predeterminada de este servicio. Aquí es cómo lo consumirías.

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

Así que eso es bastante simple. Ahora para la última parte: comprobabilidad. Este debería ser obvio, pero te mostraré cómo hacer una prueba simple para esto. Yo uso Moq para anotación, pero puedes usar lo que quieras, por supuesto.

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

Esto probablemente funcione para ti.

Hay una biblioteca en CodePlex llamada "SystemWrapper" ( http://systemwrapper.codeplex.com ) que puede evitarle tener que hacer mucho de este tipo de cosas. Parece que FileDialog aún no es compatible, por lo que definitivamente tendrá que escribir una interfaz para ese.

Espero que esto ayude.

Editar :

Me parece recordar que preferías TypeMock Isolator para tu marco falso. Aquí está la misma prueba usando 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); }

Espero que esto sea útil también.