Реализация команды "закрыть окно" с помощью MVVM

Так что моя первая попытка оказалась позади код, и теперь я пытаюсь рефакторинг мой код, чтобы использовать шаблон MVVM, руководствуясь помощью MVVM в поле информация.

Я создал класс viewmodel в соответствии с моим классом view, и я перемещаю код из кода позади в viewmodel, начиная с команд.

моя первая загвоздка пытается реализовать кнопку "Закрыть", которая закрывает окно, если данные не были модифицированный. Я настроил CloseCommand для замены метода "onClick", и все хорошо, за исключением того, где код пытается запустить this.Close(). Очевидно, поскольку код был перемещен из окна в обычный класс, " это " не является окном и поэтому не закрывается. Однако, согласно MVVM, viewmodel не знает о представлении, поэтому я не могу позвонить view.Close().

может кто-нибудь предложить, как я могу закрыть окно из команды viewmodel?

13 ответов


вам не нужно передавать экземпляр представления в слой ViewModel. Вы можете получить доступ к главному окну, как это -

Application.Current.MainWindow.Close()

Я не вижу проблем в доступе к вашему главному окну в классе ViewModel, как указано выше. В соответствии с принципом MVVM не должно быть жесткой связи между вашим видом и ViewModel, т. е. они должны работать, не обращая внимания на другие операции. Здесь мы ничего не передаем ViewModel из вида. Если вы хотите искать другие варианты, это может помочь вам - закрыть окно с помощью MVVM


я лично использую очень простой подход: для каждой ViewModel, связанной с закрытым представлением, я создал базовую ViewModel, как в следующем примере:

public abstract class CloseableViewModel
{
    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }
}

тогда в вашей ViewModel, которая наследуется от CloseableViewModel, просто this.OnClosingRequest(); на .

в виде:

public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}

мое решение закрыть окно из модели просмотра при нажатии кнопки выглядит следующим образом:

in view model

public RelayCommand CloseWindow;
Constructor()
{
    CloseWindow = new RelayCommand(CloseWin);
}

public void CloseWin(object obj)
{
    Window win = obj as Window;
    win.Close();
}

в виду, установите следующим образом

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />

Я делаю это, создавая вложенное свойство под названием DialogResult:

public static class DialogCloser
{
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window != null && (bool?)e.NewValue == true) 
                window.Close();
    }

    public static void SetDialogResult(Window target, bool? value)
    {
        target.SetValue(DialogResultProperty, value);
    }
}

затем напишите это вам XAML, в теге окна

WindowActions:DialogCloser.DialogResult="{Binding Close}"

наконец-то в ViewModel

    private bool _close;
    public bool Close
    {
        get { return _close; }
        set
        {
            if (_close == value)
                return;
            _close = value;
            NotifyPropertyChanged("Close");
        }
    }

если вы измените значение закрыть на true, окно будет закрыто

Close = True;

следите за модными парадигмами. MVVM может быть полезен, но вы действительно не должны относиться к нему как к жесткому набору правил. Используйте свое собственное суждение, когда это не имеет смысла - не используйте его.

решения, представленные здесь (за исключением решения @RV1987), являются очень хорошо пример получения вещей из рук. Вы заменяете один Close() вызов с таким огромным количеством кода, с какой целью? Вы ничего не выигрываете от перемещения закрытия код из представления в модель представления. Единственное, что вы получаете возможности для ошибок.

теперь я не говорю, что MVVM следует игнорировать. Наоборот, это может быть очень полезно. Только не переусердствуй.


это решение быстро и легко. Недостатком является то, что существует некоторая связь между слоями.

в вашей viewmodel:

public class MyWindowViewModel: ViewModelBase
{


    public Command.StandardCommand CloseCommand
    {
        get
        {
            return new Command.StandardCommand(Close);
        }
    }
    public void Close()
    {
        foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
            }
        }
    }
}

вот самое простое решение и чистое решение MVVM

Код ViewModel

public class ViewModel
{
    public Action CloseAction { get; set; }

    private void CloseCommandFunction()
    {
        CloseAction();
    }
}

вот код представления XAML

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        ViewModel vm = new ViewModel();
        this.DataContext = vm;

        vm.CloseAction = new Action(() => this.Close());
    }
}

MVVM-свет с пользовательским уведомлением сообщения, чтобы избежать окна для обработки каждого notificationmessage

в viewmodel:

public class CloseDialogMessage : NotificationMessage
{
    public CloseDialogMessage(object sender) : base(sender, "") { }
}

private void OnClose()
{
    Messenger.Default.Send(new CloseDialogMessage(this));
}

зарегистрировать сообщение в конструкторе окна:

Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
    Close();
});

Это очень похоже на ответ эолдре. Это функционально то же самое, что он просматривает ту же коллекцию Windows для окна, которое имеет модель представления в качестве своего datacontext; но я использовал RelayCommand и некоторые LINQ для достижения того же результата.

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}

использование инструментария MVVM-light:

в ViewModel:

 public void notifyWindowToClose()
{
    Messenger.Default.Send<NotificationMessage>(
        new NotificationMessage(this, "CloseWindowsBoundToMe")
    );
}

и в вид:

 Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});

это взято из ответа ken2k (спасибо!), просто добавив CloseCommand также к базе CloseableViewModel.

public class CloseableViewModel
{
    public CloseableViewModel()
    {
        CloseCommand = new RelayCommand(this.OnClosingRequest);
    }

    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }

    public RelayCommand CloseCommand
    {
        get;
        private set;
    }
}

ваш вид модели, наследует его

public class MyViewModel : CloseableViewModel

тогда на вас посмотреть

public MyView()
{
    var viewModel = new StudyDataStructureViewModel(studyId);
    this.DataContext = viewModel;

    //InitializeComponent(); ...

    viewModel.ClosingRequest += (sender, e) => this.Close();
}

учитывая путь, пожалуйста, проверьте

https://stackoverflow.com/a/30546407/3659387

Краткое Описание

  1. выведите свою ViewModel из INotifyPropertyChanged
  2. создайте наблюдаемое свойство CloseDialog в ViewModel, измените свойство CloseDialog всякий раз, когда вы хотите закрыть диалоговое окно.
  3. прикрепите обработчик для этого изменения свойства
  4. теперь вы почти закончили. В обработчике событий сделать DialogResult = true

прежде всего дайте вашему окну имя, как

x:Name="AboutViewWindow"

на моей кнопке закрытия я определил команду и Параметр команды, как

CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"

тогда в моем представлении модель

private ICommand _cancelCommand;        
public ICommand CancelCommand       
{
   get          
     {
        if (_cancelCommand == null)
           {
              _cancelCommand = new DelegateCommand<Window>(
                    x =>
                    {
                        x?.Close();
                    });
            }

            return _cancelCommand;          
     }      
}