Диалог открытия файла MVVM

Хорошо, я действительно хотел бы знать, как опытные разработчики MVVM обрабатывают диалог openfile в WPF.

Я действительно не хочу делать это в моей ViewModel (где на "Browse" ссылается посредством DelegateCommand)

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

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

Потому что я считаю, что это идет вразрез с методологией MVVM.

Что я делаю?

 amutter24 апр. 2013 г., 12:04
Смотрите сообщение в блоге Олли Ричес & lt;awkwardcoder.blogspot.nl/2012/03/…& GT; где объясняется, как передавать сообщения в WPF с чистым разделением View & amp; ViewModel.

Ответы на вопрос(6)

In ViewModel I have defined an interface and work with it in ViewModel In View I have implemented this interface.

CommandImpl не реализован в коде ниже.

ViewModel:
namespace ViewModels.Interfaces
{
    using System.Collections.Generic;
    public interface IDialogWindow
    {
        List<string> ExecuteFileDialog(object owner, string extFilter);
    }
}

namespace ViewModels
{
    using ViewModels.Interfaces;
    public class MyViewModel
    {
        public ICommand DoSomeThingCmd { get; } = new CommandImpl((dialogType) =>
        {
            var dlgObj = Activator.CreateInstance(dialogType) as IDialogWindow;
            var fileNames = dlgObj?.ExecuteFileDialog(null, "*.txt");
            //Do something with fileNames..
        });
    }
}
View:
namespace Views
{
    using ViewModels.Interfaces;
    using Microsoft.Win32;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;

    public class OpenFilesDialog : IDialogWindow
    {
        public List<string> ExecuteFileDialog(object owner, string extFilter)
        {
            var fd = new OpenFileDialog();
            fd.Multiselect = true;
            if (!string.IsNullOrWhiteSpace(extFilter))
            {
                fd.Filter = extFilter;
            }
            fd.ShowDialog(owner as Window);

            return fd.FileNames.ToList();
        }
    }
}

XAML:

<Window

    xmlns:views="clr-namespace:Views"
    xmlns:viewModels="clr-namespace:ViewModels"
>    
    <Window.DataContext>
        <viewModels:MyViewModel/>
    </Window.DataContext>

    <Grid>
        <Button Content = "Open files.." Command="{Binding DoSomeThingCmd}" CommandParameter="{x:Type views:OpenFilesDialog}"/>
    </Grid>
</Window>

ии. Если виртуальная машина размещена в отдельной DLL, проект не должен иметь ссылку на PresentationFramework.

Мне нравится использовать вспомогательный класс в представлении для общих диалогов.

Вспомогательный класс предоставляет команду (не событие), с которой окно связывается в XAML. Это подразумевает использование RelayCommand в представлении. Вспомогательный класс является DepencyObject, поэтому он может связываться с моделью представления.

class DialogHelper : DependencyObject
{
    public ViewModel ViewModel
    {
        get { return (ViewModel)GetValue(ViewMo,delProperty); }
        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;
        }
    }
}

Вспомогательный класс нуждается в ссылке на экземпляр ViewModel. Смотрите словарь ресурсов. Сразу после построения устанавливается свойство ViewModel (в той же строке XAML). Это когда свойство FileName в классе помощника связано со свойством FileName в модели представления.

<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>
 09 июн. 2014 г., 21:10
Либо будет работать, но свойства CLR легче, поэтому я предпочитаю их, где это возможно. Это не имеет большого значения, хотя. Помните, что объект, к которому вы привязываете, может быть простым свойством CLR. (Но, конечно, то, что вы связываете СО, должно быть DP, именно поэтому FileName является DP.)
 09 июн. 2014 г., 15:47
Просто любопытно, (я все еще изучаю WPF), почему вы не сделали публичный ICommand OpenFile {get; приватный набор; } & Quot; свойство зависимости? Мне трудно понять, когда сделать что-то свойством зависимости, а когда все нормально, иметь простой объект .Net.
 03 мая 2017 г., 08:52
Я не согласен с вами, что "ViewModel не должен открывать диалоги или даже знать об их существовании". Существуют такие сценарии, когда в середине кода команды вам нужно открыть диалоговое окно файла или то же самое. Вам не нужно иметь ссылки на классы, определенные в слое View, но вы можете иметь ссылки для просмотра объектов во время выполнения иwork with interfaces defined in ViewModel and implemented in View, Увидеть:stackoverflow.com/questions/42931775/is-mvvm-pattern-broken

но, увы, моя репутация недостаточно высока, чтобы сделать это.

Наличие вызова, такого как OpenFileDialog (), нарушает шаблон MVVM, поскольку подразумевает представление (диалог) в модели представления. Модель представления может вызывать что-то вроде GetFileName () (то есть, если простого связывания недостаточно), но не должно заботиться о том, как получить имя файла.

Решение Вопроса

Служба - это просто класс, к которому вы получаете доступ из центрального хранилища служб, часто из контейнера IOC. Затем сервис реализует то, что вам нужно, например OpenFileDialog.

Итак, если у вас естьIFileDialogService в контейнере Unity вы можете сделать ...

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

    string path = fileDialogService.OpenFileDialog();

    if (!string.IsNullOrEmpty(path))
    {
        //Do stuff
    }
}
 25 янв. 2012 г., 18:34
Как вы говорите XAML использовать этот сервис?
 26 янв. 2012 г., 15:16
В зависимости от вашей инфраструктуры MVVM вы можете использовать команду или действие для выполнения кода на основе нажатия кнопки или какого-либо другого события.
 14 янв. 2014 г., 07:17
@CameronMacFarland Я немного медленный, не могли бы вы показать мне пример интерфейса и как он вызывается в средстве просмотра моделей?
 29 дек. 2016 г., 11:36
Хотя решение является правильным, оно представляет собой анти-шаблон Service Locator. Вместо разрешенияIFileDialogService, это должно быть передано как зависимость в ctor.
 19 июл. 2013 г., 22:12
Есть ли способ указать окно, которому принадлежит диалог?

который я, например, могу передать в конструктор моего viewModel или разрешить с помощью внедрения зависимости. например

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

и класс, реализующий это, используя OpenFileDialog под капотом. В viewModel я использую только интерфейс и, таким образом, могу смоделировать / заменить его при необходимости.

 14 дек. 2014 г., 11:49
this is the same pattern that the .NET Framework uses Да, для этого открытый файлdialogs, Ни WinForms, ни WPF не поддерживают межпотоковый доступ, поскольку это реализовано в .NET Framework, и проблема в этом спорная.
 29 нояб. 2009 г., 21:46
Хотя это и правда, я согласен с оценкой Кэмерона здесь. Вы рискуете непреднамеренных побочных эффектов с подходом собственности. Я знаю, что это тот же шаблон, который использует .NET Framework, но это не обязательно аргумент для этого шаблона, просто он существует. Я предпочитаю использовать проверку на нулевое значение в возвращаемом значении, чтобы указать на какой-либо сбой (или пользователь нажал "Отмена").
 25 июн. 2009 г., 16:50
В контейнерах внедрения зависимостей вы обычно можете зарегистрировать его, чтобы при каждом разрешении службы создавался новый экземпляр. Таким образом, вы можете создать новый сервис, использовать его и выбросить после этого. Или вы используете только метод OpenFileDialog () и позволяете ему вернуть строку, или ноль, если он был прерван.
 25 июн. 2009 г., 16:43
Моя единственная проблема со свойствами экземпляра в сервисах - это как быть, если два потока вызывают OpenFileDialog одновременно? Не совсем проблема для файловых диалогов, но может быть проблема для других типов сервисов.

что открывать вид из модели представления. Я имею в виду свойство Dependency, и в chnage свойства я открываю FileDialog и читаю путь, обновляю свойство и, следовательно, свойство bound виртуальной машины.

Ваш ответ на вопрос