ViewModel应该如何关闭表单?
我试图学习WPF和MVVM问题,但遇到了一些障碍。 这个问题与这个问题类似但不完全相同(处理-wpf-with-mvvm中的对话框)...
我有一个使用MVVM模式编写的“登录”表单。
这个表单有一个ViewModel,它保存着用户名和密码,它们使用普通的数据绑定绑定到XAML中的视图。 它还有一个“登录”命令,它绑定到表单上的“登录”按钮,使用普通的数据绑定。
当“Login”命令触发时,它会调用ViewModel中的一个函数,该函数将关闭并通过网络发送数据以进行登录。当此函数完成时,会执行两个操作:
登录无效 - 我们只显示一个MessageBox,一切正常
登录是有效的,我们需要关闭登录表单并使其作为其DialogResult
返回true ...
问题是,ViewModel对实际视图一无所知,所以它如何关闭视图并告诉它返回一个特定的DialogResult? 我可以在CodeBehind中添加一些代码,并且/或者将View传递给ViewModel,但是这看起来好像会彻底击败MVVM的全部...
更新
最后,我刚刚违反了MVVM模式的“纯度”,并让View发布了一个Closed
事件,并公开了一个Close
方法。 ViewModel然后会调用view.Close
。 该视图只能通过接口获知并通过IOC容器连接,因此不会损失可测试性或可维护性。
看起来相当愚蠢的是,接受的答案是-5票! 虽然我很清楚通过解决问题而获得的良好感受,同时又是“纯粹的”,但我并不是唯一认为200行事件,命令和行为只是为了避免单行方法“模式”和“纯度”的名字有点荒谬....
我从Thejuan的答案中得到启发,写出一个更简单的附加属性。 没有风格,没有触发器; 相反,你可以这样做:
<Window ...
xmlns:xc="clr-namespace:ExCastle.Wpf"
xc:DialogCloser.DialogResult="{Binding DialogResult}">
这几乎就像WPF团队正确地做到了一样清晰,并且首先将DialogResult作为依赖项属性。 只需要一个bool? DialogResult
您的ViewModel上的bool? DialogResult
属性并实现INotifyPropertyChanged,而您的ViewModel只需通过设置属性即可关闭窗口(并设置它的DialogResult)。 MVVM,因为它应该是。
以下是DialogCloser的代码:
using System.Windows;
namespace ExCastle.Wpf
{
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)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
}
我也在我的博客上发布了这个。
从我的角度来看,这个问题非常好,因为相同的方法不仅适用于“登录”窗口,而且适用于任何类型的窗口。 我已经通过了很多建议,没有人对我好。 请参阅我的类,这是从MVVM设计模式文章中获取的。
每个ViewModel类应该被继承WorkspaceViewModel
有RequestClose
envent和CloseCommand
的财产ICommand
类型。 在缺省实现CloseCommand
属性将提高RequestClose
事件。
为了让窗口关闭,窗口的OnLoaded
方法应该被忽略:
void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
DataContext = customer;
customer.RequestClose += () => { Close(); };
}
或您的应用的OnStartup
方法:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
viewModel.RequestClose += window.Close;
window.DataContext = viewModel;
window.Show();
}
我想这RequestClose
事件和CloseCommand
属性实现在WorkspaceViewModel
是相当明确的,但我会告诉他们是一致的:
public abstract class WorkspaceViewModel : ViewModelBase // There are nothing interest in ViewModelBase, it only implements INotifyPropertyChanged interface only
{
RelayCommand _closeCommand;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand(
param => Close(),
param => CanClose()
);
}
return _closeCommand;
}
}
public event Action RequestClose;
public virtual void Close()
{
if ( RequestClose!=null )
{
RequestClose();
}
}
public virtual bool CanClose()
{
return true;
}
}
以及RelayCommand
的源代码:
public class RelayCommand : ICommand
{
#region Constructors
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
}
PS不要善待我这些消息来源,如果我昨天有那样会为我节省几个小时......
PPS欢迎任何意见或建议。
我使用附加的行为来关闭窗口。 将ViewModel上的“signal”属性绑定到附加的行为上(我实际上使用触发器)当它设置为true时,行为会关闭窗口。
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
链接地址: http://www.djcxy.com/p/20271.html上一篇: How should the ViewModel close the form?
下一篇: ClassNotFoundException oracle.jdbc.driver.OracleDriver only in servlet, using Eclipse