Выполнение команды "закрыть окно" с MVVM
Итак, моя первая попытка сделала все из кода позади, и теперь я пытаюсь реорганизовать мой код, чтобы использовать шаблон MVVM, следуя указаниям MVVM в информации о коробке.
Я создал класс viewmodel для соответствия моему классу представления, и я перемещаю код из кода в viewmodel, начиная с команд.
Моя первая проблема - реализовать кнопку "Закрыть", которая закрывает окно, если данные не были изменены. Я установил CloseCommand для замены метода 'onClick', и все хорошо, за исключением тех случаев, когда код пытается выполнить this.Close()
. Очевидно, что поскольку код был перемещен из окна в обычный класс, "this" не является окном и поэтому не может быть закрыто. Однако, согласно MVVM, модель представления не знает о представлении, поэтому я не могу вызвать view.Close()
.
Может кто-нибудь подсказать, как можно закрыть окно из команды viewmodel?
Ответы
Ответ 1
Вам не нужно передавать экземпляр View на ваш уровень ViewModel. Вы можете получить доступ к главному окну, например:
Application.Current.MainWindow.Close()
Я не вижу проблемы при доступе к вашему главному окну в классе ViewModel, как указано выше. В соответствии с принципом MVVM не должно быть жесткой связи между вашим представлением и ViewModel, то есть они должны работать, не обращая внимания на другие операции. Здесь мы ничего не передаем ViewModel из View. Если вы хотите найти другие варианты, это может вам помочь - Закрыть окно с помощью MVVM
Ответ 2
Я лично использую очень простой подход: для каждого 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();
для команды Close
.
В представлении:
public class YourView
{
...
var vm = new ClosableViewModel();
this.Datacontext = vm;
vm.ClosingRequest += (sender, e) => this.Close();
}
Ответ 3
Мое решение закрыть окно из модели просмотра при нажатии кнопки выглядит следующим образом:
В представлении модели
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" />
Ответ 4
Я делаю это, создавая прикрепленное свойство 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");
}
}
если вы измените Close на true, окно будет закрыто
Close = True;
Ответ 5
Остерегайтесь модных парадигм. MVVM может быть полезным, но вы действительно не должны относиться к нему как к жесткому набору правил. Используйте свое собственное мнение, и когда это не имеет смысла - не используйте его.
Решения, предлагаемые здесь (за исключением решения @RV1987), являются очень хорошим примером того, как выходить из рук. Вы заменяете один вызов Close()
таким огромным количеством кода, с какой целью? Вы ничего не получаете от перемещения закрывающего кода из представления в модель представления. Единственное, что вы получаете, - это место для большего количества ошибок.
Теперь я не говорю, что MVVM следует игнорировать. Наоборот, это может быть очень полезно. Просто не делай этого.
Ответ 6
Вот самое простое и чистое решение MVVM
ViewModel Code
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());
}
}
Ответ 7
Это быстрое и простое решение. Недостатком является то, что между слоями существует некоторая связь.
В вашей модели просмотра:
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();
}
}
}
}
Ответ 8
MVVM-light с уведомлением пользовательского сообщения, чтобы избежать окна для обработки каждого сообщения уведомления
В 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();
});
Ответ 9
Это очень похоже на ответ eoldre. Это функционально то же самое в том, что он просматривает ту же коллекцию Windows для окна, которое имеет модель представления в качестве своего datacontext; но я использовал RelayCommand и некоторые LINQ для достижения того же результата.
public RelayCommand CloseCommand
{
get
{
return new RelayCommand(() => Application.Current.Windows
.Cast<Window>()
.Single(w => w.DataContext == this)
.Close());
}
}
Ответ 10
с использованием инструментария 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();
}
});
Ответ 11
Это взято из ответа 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();
}
Ответ 12
Учитывая способ, пожалуйста, проверьте
fooobar.com/questions/29614/...
Краткое описание
- Вывести свою ViewModel из INotifyPropertyChanged
- Создайте наблюдаемое свойство CloseDialog в ViewModel, измените свойство CloseDialog, когда вы хотите закрыть диалог.
- Примените обработчик в представлении для этого изменения свойства
- Теперь вы почти закончили. В обработчике событий make DialogResult = true
Ответ 13
прежде всего дайте вашему окну такое имя, как
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;
}
}