所以我的第一次尝试完成了后面的代码,现在我正在尝试重构我的代码以使用MVVM模式,遵循MVVM in the box信息的指导。
我已经创建了一个viewmodel类来匹配我的视图类,我正在将代码从代码中移出到viewmodel中,从命令开始。
我的第一个障碍是尝试实现一个“关闭”按钮,如果数据未被修改则关闭窗口。我已经装配了一个CloseCommand来替换'onClick'方法,除了代码试图运行this.Close()
的地方外,一切都很好。显然,由于代码已从窗口移动到普通类,因此“this”不是窗口,因此不可关闭。但是,根据MVVM,viewmodel不知道该视图,因此我无法调用view.Close()
。
有人可以建议我如何从viewmodel命令关闭窗口?
答案 0 :(得分:58)
我个人使用一种非常简单的方法:对于与可关闭View相关的每个ViewModel,我创建了一个基本ViewModel,如下例所示:
public abstract class CloseableViewModel
{
public event EventHandler ClosingRequest;
protected void OnClosingRequest()
{
if (this.ClosingRequest != null)
{
this.ClosingRequest(this, EventArgs.Empty);
}
}
}
然后在继承自CloseableViewModel
的ViewModel中,只需为this.OnClosingRequest();
命令调用Close
。
在视图中:
public class YourView
{
...
var vm = new ClosableViewModel();
this.Datacontext = vm;
vm.ClosingRequest += (sender, e) => this.Close();
}
答案 1 :(得分:29)
您无需将View实例传递给ViewModel图层。您可以像这样访问主窗口 -
Application.Current.MainWindow.Close()
我发现在ViewModel类中访问主窗口没有问题,如上所述。根据MVVM原则,View和ViewModel之间不应该存在紧密耦合,即它们应该忽略其他操作。在这里,我们没有从View向ViewModel传递任何内容。如果您想寻找其他选项,这可能会对您有所帮助 - Close window using MVVM
答案 2 :(得分:25)
我点击按钮时从视图模型关闭窗口的解决方案如下:
在视图模型中
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" />
答案 3 :(得分:12)
我是通过创建一个名为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);
}
}
然后在窗口标记
中将此内容写入XAMLWindowActions: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;
答案 4 :(得分:10)
注意时尚范例。 MVVM可能很有用,但你真的不应该将它视为一套严格的规则。使用你自己的判断,当它没有意义时 - 不要使用它。
此处提供的解决方案(@ RV1987的解决方案除外)是非常好的举例失控的例子。您正在用如此大量的代码替换单个Close()
调用,目的是什么?通过将结束代码从视图移动到视图模型,您完全没有任何好处。你获得的唯一的东西是更多的错误的空间。
现在,我不是说要忽略MVVM。恰恰相反,它非常有用。只是不要过度。
答案 5 :(得分:6)
这是最简单,最纯粹的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());
}
}
答案 6 :(得分:5)
此解决方案快速简便。缺点是层之间存在一些耦合。
在你的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();
}
}
}
}
答案 7 :(得分:3)
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();
});
答案 8 :(得分:2)
这与eoldre的答案非常相似。它在功能上是相同的,因为它通过相同的Windows集合查找具有视图模型作为其datacontext的窗口;但是我使用了RelayCommand和一些LINQ来实现相同的结果。
public RelayCommand CloseCommand
{
get
{
return new RelayCommand(() => Application.Current.Windows
.Cast<Window>()
.Single(w => w.DataContext == this)
.Close());
}
}
答案 9 :(得分:2)
使用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();
}
});
答案 10 :(得分:0)
这取自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();
}
答案 11 :(得分:0)
如果有办法,请检查
https://stackoverflow.com/a/30546407/3659387
简短说明
答案 12 :(得分:0)
首先给你的窗口命名为
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;
}
}