60 votes

Boîte de dialogue Ouvrir un fichier

Ok, j'aimerais vraiment savoir comment les développeurs experts MVVM gèrent un dialogue openfile dans WPF.

Je ne veux pas vraiment faire cela dans mon ViewModel (où 'Browse' est référencé via une DelegateCommand)

 void Browse(object param)
{
    //Add code here
    OpenFileDialog d = new OpenFileDialog();

    if (d.ShowDialog() == true)
    {
        //Do stuff
    }
}
 

Parce que je crois que cela va à l’encontre de la méthodologie MVVM.

Que fais-je?

37voto

Cameron MacFarland Points 27240

La meilleure chose à faire ici est d'utiliser un service.

Un service est simplement une classe à laquelle vous accédez depuis un référentiel central de services, souvent un conteneur IOC. Le service implémente ensuite ce dont vous avez besoin, comme OpenFileDialog.

Donc, en supposant que vous ayez un IFileDialogService dans un conteneur Unity, vous pourriez le faire ...

 void Browse(object param)
{
    var fileDialogService = container.Resolve<IFileDialogService>();

    string path = fileDialogService.OpenFileDialog();

    if (!string.IsNullOrEmpty(path))
    {
        //Do stuff
    }
}
 

12voto

JAB Points 66

J'aurais aimé commenter l'une des réponses mais, hélas, ma réputation n'est pas assez grande pour le faire.

Avoir un appel tel que OpenFileDialog () viole le modèle MVVM car il implique une vue (dialogue) dans le modèle de vue. Le modèle de vue peut appeler quelque chose comme GetFileName () (c'est-à-dire si une simple liaison n'est pas suffisante), mais il ne doit pas se préoccuper de la façon dont le nom de fichier est obtenu.

9voto

Ima Dirty Troll Points 479

Le ViewModel ne devrait pas ouvrir les boîtes de dialogue ou même de connaître leur existence. Si la VM est logé dans une DLL, le projet ne devrait pas avoir une référence à PresentationFramework.

J'aime utiliser une classe d'assistance dans l'affichage des boîtes de dialogue communes.

La classe helper expose une commande (pas un événement) de la fenêtre, qui se lie à en XAML. Ceci implique l'utilisation de RelayCommand au sein de la vue. La classe helper est un DepencyObject de sorte qu'il peut se lier à la vue du modèle.

class DialogHelper : DependencyObject
{
    public ViewModel ViewModel
    {
        get { return (ViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(ViewModel), typeof(DialogHelper),
        new UIPropertyMetadata(new PropertyChangedCallback(ViewModelProperty_Changed)));

    private static void ViewModelProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (ViewModelProperty != null)
        {
            Binding myBinding = new Binding("FileName");
            myBinding.Source = e.NewValue;
            myBinding.Mode = BindingMode.OneWayToSource;
            BindingOperations.SetBinding(d, FileNameProperty, myBinding);
        }
    }

    private string FileName
    {
        get { return (string)GetValue(FileNameProperty); }
        set { SetValue(FileNameProperty, value); }
    }

    private static readonly DependencyProperty FileNameProperty =
        DependencyProperty.Register("FileName", typeof(string), typeof(DialogHelper),
        new UIPropertyMetadata(new PropertyChangedCallback(FileNameProperty_Changed)));

    private static void FileNameProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("DialogHelper.FileName = {0}", e.NewValue);
    }

    public ICommand OpenFile { get; private set; }

    public DialogHelper()
    {
        OpenFile = new RelayCommand(OpenFileAction);
    }

    private void OpenFileAction(object obj)
    {
        OpenFileDialog dlg = new OpenFileDialog();

        if (dlg.ShowDialog() == true)
        {
            FileName = dlg.FileName;
        }
    }
}

La classe helper a besoin d'une référence pour le ViewModel instance. Voir le dictionnaire de ressources. Juste après la construction, le ViewModel propriété est définie (dans la même ligne de code XAML). C'est lorsque le nom de la propriété sur la classe helper est lié à la propriété FileName sur le modèle de vue.

<Window x:Class="DialogExperiment.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DialogExperiment"
        xmlns:vm="clr-namespace:DialogExperimentVM;assembly=DialogExperimentVM"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <vm:ViewModel x:Key="viewModel" />
        <local:DialogHelper x:Key="helper" ViewModel="{StaticResource viewModel}"/>
    </Window.Resources>
    <DockPanel DataContext="{StaticResource viewModel}">
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Open" Command="{Binding Source={StaticResource helper}, Path=OpenFile}" />
            </MenuItem>
        </Menu>
    </DockPanel>
</Window>

8voto

Botz3000 Points 23640

J'utilise un service que je peux par exemple transmettre au constructeur de mon viewModel ou résoudre via une injection de dépendance. par exemple

 public interface IOpenFileService
{
    string FileName { get; }
    bool OpenFileDialog()
}
 

et une classe l'implémentant, utilisant OpenFileDialog sous le capot. Dans le viewModel, je n'utilise que l'interface et je peux donc la simuler / la remplacer si nécessaire.

3voto

Jilt Points 21

Avoir un service, c'est comme ouvrir une vue de viewmodel. J'ai une propriété Dependency en vue, et sur le chnage de la propriété, j'ouvre FileDialog et lis le chemin, mets à jour la propriété et par conséquent la propriété liée de la VM

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