Frage

Ich habe gerade begonnen, die MVVM Muster für WPF zu lernen. Ich traf eine Wand: Was tun Sie, wenn Sie eine Openfile zeigen müssen

Hier ist ein Beispiel UI Ich versuche, es zu benutzen auf:

alt text

Wenn die Browse-Schaltfläche geklickt wird, soll ein Openfiledialog angezeigt. Wenn der Benutzer eine Datei aus dem Openfile auswählt, sollte der Dateipfad in dem Textfeld angezeigt werden.

Wie kann ich das mit MVVM?

Aktualisieren : Wie kann ich dies tun, mit MVVM und mache es Unit-Test-fähig? Die Lösung unten funktioniert nicht für Unit-Tests.

War es hilfreich?

Lösung

Was ich in der Regel tun, ist eine Schnittstelle für einen Anwendungsdienst erstellen, die diese Funktion ausführt. In meinen Beispielen werde ich annehmen, dass Sie so etwas wie das MVVM Toolkit oder etwas ähnliches verwenden (so kann ich eine Basis Ansichtsmodell und eine RelayCommand zu bekommen).

Hier ist ein Beispiel für eine sehr einfache Schnittstelle für den Grund IO-Operationen wie Openfile und Openfile zu tun. Ich bin sie beide hier zeigen, so dass Sie glaube nicht, dass ich vorschlage Sie eine Schnittstelle mit einer Methode erstellen, um dieses Problem zu erhalten.

public interface IOService
{
     string OpenFileDialog(string defaultPath);

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

In Ihrer Anwendung würden Sie eine Standardimplementierung dieses Service bieten. Hier ist, wie Sie es verbrauchen würde.

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

So, das ist ziemlich einfach. Jetzt für den letzten Teil: Überprüfbarkeit. Dieser sollte offensichtlich sein, aber ich werde Ihnen zeigen, wie man einen einfachen Test für diese machen. Ich benutze Moq für Anstoßen, aber Sie können verwenden, was Sie natürlich möchten.

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

Dies wird wahrscheinlich für Sie arbeiten.

Es gibt eine Bibliothek aus auf CodePlex namens "SystemWrapper" ( http://systemwrapper.codeplex.com ) das könnte Sie davon ab, speichern Sie eine Los von dieser Art der Sache zu tun. Es sieht aus wie File-Dialog wird noch nicht unterstützt, so dass Sie auf jeden Fall eine Schnittstelle für diesen einen schreiben.

Hope, das hilft.

Bearbeiten :

Ich scheine Sie begünstigende TypeMock Trenner für Ihren Fälschen Rahmen zu erinnern. Hier ist der gleiche Test mit Trenner:

[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 dies hilfreich ist auch.

Andere Tipps

Die WPF Application Framework (WAF) stellt eine Implementierung für das Öffnen und Savefiledialog.

Die Writer-Beispielanwendung zeigt, wie sie verwenden und wie die Code-Einheit getestet werden.

Als erstes würde ich Ihnen empfehlen, mit einem WPF MVVM Toolkit . Dies gibt Ihnen eine schöne Auswahl an Befehlen für Ihre Projekte zu verwenden. Eine Besonderheit, die seit dem MVVM Muster der Einführung berühmt gemacht wurde, ist der RelayCommand (es gibt manny andere Versionen natürlich, aber ich bleibe nur auf die am häufigsten verwendeten). Seine eine Implementierung der ICommand-Schnittstelle, die Ihnen Kiste einen neuen Befehl in Ihrem Ansichtsmodell ermöglicht.

Zurück zu Ihrer Frage, hier ist ein Beispiel dafür, was Ihr Viewmodel aussehen kann.

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 und RelayCommand sind beide aus der MVVM Toolkit . Hier ist, was die XAML aussehen kann.

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

und Ihr XAML.CS Code zurück.

DataContext = new OpenFileDialogVM();
InitializeComponent();

Das ist es.

Wie Sie besser vertraut mit den Befehlen zu erhalten, können Sie auch Bedingungen wie zu setzen, wenn Sie auf die Schaltfläche Durchsuchen deaktiviert sein, usw. Ich hoffe, dass darauf Sie in die Richtung, die Sie wollten.

Aus meiner Sicht ist die beste Option ist das Prisma Bibliothek und InteractionRequests. Die Aktion des Dialog zu öffnen bleibt innerhalb der XAML und wird von Ansichtsmodell ausgelöst, während das Ansichtsmodell braucht nichts über die Aussicht kennen.

Siehe auch

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

Als Beispiel siehe:

https: // Github .com / plainionist / Plainion.Prism / Blob / Master / src / Plainion.Prism / Inter / PopupCommonDialogAction.cs

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

Meiner Meinung nach die beste Lösung, die eine individuelle Steuerung zu schaffen.

Die individuelle Steuerung ich in der Regel erstellen besteht aus:

  • Textbox oder Textblock
  • Button mit einem Bild als Vorlage
  • String-Abhängigkeitseigenschaft, wo der Dateipfad wird zu verpackenden

So ist die * XAML-Datei so sein würde

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

Und die * CS-Datei:

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

Am Ende Sie es Ihrer Ansicht nach Modell binden können:

<controls:customFilePicker Text="{Binding Text}"/>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top