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类应该被继承WorkspaceViewModelRequestClose 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