Pergunta

Eu só comecei a aprender o padrão MVVM para WPF. Eu bati em uma parede:? o que você faz quando você precisa mostrar um OpenFileDialog

Aqui está um exemplo UI Eu estou tentando usá-lo em:

text alt

Quando o botão de navegação é clicado, um OpenFileDialog deve ser mostrado. Quando o usuário seleciona um arquivo do OpenFileDialog, o caminho do arquivo deve ser exibido na caixa de texto.

Como posso fazer isso com MVVM?

Atualizar : Como posso fazer isso com MVVM e torná-lo unidade de teste-poder? A solução abaixo não funciona para testes de unidade.

Foi útil?

Solução

O que eu geralmente faço é criar uma interface para um serviço de aplicativo que executa esta função. Nos meus exemplos eu vou assumir que você está usando algo parecido com o MVVM Toolkit ou coisa semelhante (para que eu possa obter uma base ViewModel e uma RelayCommand).

Aqui está um exemplo de uma interface extremamente simples para fazer operações básicas de IO como OpenFileDialog e OpenFile. Eu estou mostrando-lhes tanto aqui, assim você não acha que eu estou sugerindo que você criar uma interface com um método para contornar este problema.

public interface IOService
{
     string OpenFileDialog(string defaultPath);

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

Em seu aplicativo, você deve fornecer uma implementação padrão desse serviço. Aqui está como você consumi-lo.

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

Então, isso é muito simples. Agora para a última parte: a capacidade de teste. Este deve ser óbvio, mas eu vou lhe mostrar como fazer um teste simples para isso. Eu uso Moq para stubbing, mas você pode usar o que quiser, é claro.

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

Este provavelmente trabalho para você.

Há um fora biblioteca no CodePlex chamado "SystemWrapper" ( http://systemwrapper.codeplex.com ) que pode salvá-lo de ter que fazer um muito desse tipo de coisa. Parece que FileDialog ainda não é suportado, então você definitivamente tem que escrever uma interface para isso.

Espero que isso ajude.

Editar :

Eu me lembro de você favorecendo TypeMock Isolador para o seu quadro de falsificar. Aqui está o mesmo teste usando Isolador:

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

Hope isso é útil também.

Outras dicas

O WPF Application Framework (WAF) fornece uma implementação para o Aberto e SaveFileDialog.

A amostra escritor mostra aplicações como usá-los e como o código pode ser unidade testada.

Em primeiro lugar eu recomendo que você começar com um WPF MVVM Toolkit . Isto dá-lhe uma boa selecção de comandos para usar para seus projetos. Uma característica particular que tem sido feito famosa desde a introdução do padrão MVVM é o RelayCommand (há Manny outras versões do curso, mas eu só manter o mais comumente usado). Seu uma implementação da interface ICommand que lhe permite caixote um novo comando em seu ViewModel.

De volta à sua pergunta, aqui está um exemplo do que o seu ViewModel pode parecer.

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 e RelayCommand são ambos do MVVM Toolkit . Aqui está o que o XAML pode parecer.

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

e seu código por trás XAML.CS.

DataContext = new OpenFileDialogVM();
InitializeComponent();

É isso.

Como você ficar mais familiarizado com os comandos, você também pode definir as condições de quando você deseja que o botão Procurar para ser desativada, etc. Espero que você apontou na direção que você queria.

Da minha perspectiva, a melhor opção é a biblioteca prisma e InteractionRequests. A ação para abrir os restos de diálogo dentro do XAML e é acionado a partir viewmodel enquanto o ViewModel não precisa saber nada sobre a vista.

Veja também

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

Como exemplo, consulte:

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

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

Na minha opinião, a melhor solução é criar um controle personalizado.

O controle personalizado Eu costumo criar é composto de:

  • Caixa de texto ou textblock
  • O botão com uma imagem como modelo
  • Cordas propriedade de dependência onde o caminho do arquivo será embrulhado para

Então, o arquivo XAML * seria assim

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

E o ficheiro.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;
    }
}

No final você pode vinculá-lo a seu modelo de vista:

<controls:customFilePicker Text="{Binding Text}"/>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top