Реализация команды "закрыть окно" с помощью 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
Краткое Описание
- выведите свою ViewModel из INotifyPropertyChanged
- создайте наблюдаемое свойство CloseDialog в ViewModel, измените свойство CloseDialog всякий раз, когда вы хотите закрыть диалоговое окно.
- прикрепите обработчик для этого изменения свойства
- теперь вы почти закончили. В обработчике событий сделать 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;
}
}