代码之家  ›  专栏  ›  技术社区  ›  Olivier Payen Sackurise

用WPF/mvvmlighttoolkit处理窗口关闭事件

  •  131
  • Olivier Payen Sackurise  · 技术社区  · 15 年前

    Closing 事件(当用户单击右上角的“X”按钮时),以便最终显示确认消息或/和取消关闭。

    我知道如何在代码隐藏中做到这一点:订阅 交割 事件,然后使用 CancelEventArgs.Cancel 财产。

    但我使用的是MVVM,所以我不确定这是个好方法。

    我认为最好的办法是 事件到a Command

    我试过了:

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <cmd:EventToCommand Command="{Binding CloseCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    

    RelayCommand 在我的ViewModel中,但它不起作用(命令的代码不执行)。

    12 回复  |  直到 6 年前
        1
  •  127
  •   Eliahu Aaron    6 年前

    我只需在视图构造函数中关联处理程序:

    MyWindow() 
    {
        // Set up ViewModel, assign to DataContext etc.
        Closing += viewModel.OnWindowClosing;
    }
    

    然后将处理程序添加到 ViewModel :

    using System.ComponentModel;
    
    public void OnWindowClosing(object sender, CancelEventArgs e) 
    {
       // Handle closing logic, set e.Cancel as needed
    }
    

    在本例中,通过使用更复杂的模式和更间接的方式(额外的5行xamlplus),您只获得了复杂性 Command 模式)。

    . 即使事件绑定在视图后面的代码中 不依赖于视图和结束逻辑 可以进行单元测试 .

        2
  •  81
  •   Eliahu Aaron    6 年前

    这个代码很好用:

    视图模型.cs:

    public ICommand WindowClosing
    {
        get
        {
            return new RelayCommand<CancelEventArgs>(
                (args) =>{
                         });
        }
    }
    

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <command:EventToCommand Command="{Binding WindowClosing}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    

    假设:

    • ViewModel被指定给 DataContext
    • xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL5"
    • xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        3
  •  34
  •   PILuaces    14 年前

    这个选项更容易,也许适合你。在视图模型构造函数中,可以订阅主窗口关闭事件,如下所示:

    Application.Current.MainWindow.Closing += new CancelEventHandler(MainWindow_Closing);
    
    void MainWindow_Closing(object sender, CancelEventArgs e)
    {
                //Your code to handle the event
    }
    

    祝你一切顺利。

        4
  •  16
  •   AxdorphCoder    11 年前

    如果您不想知道ViewModel中的窗口(或它的任何事件),这里有一个根据MVVM模式的答案。

    public interface IClosing
    {
        /// <summary>
        /// Executes when window is closing
        /// </summary>
        /// <returns>Whether the windows should be closed by the caller</returns>
        bool OnClosing();
    }
    

    在ViewModel中添加接口和实现

    public bool OnClosing()
    {
        bool close = true;
    
        //Ask whether to save changes och cancel etc
        //close = false; //If you want to cancel close
    
        return close;
    }
    

    在窗口中我添加了结束事件。这种代码隐藏不会破坏MVVM模式。视图可以知道viewmodel!

    void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        IClosing context = DataContext as IClosing;
        if (context != null)
        {
            e.Cancel = !context.OnClosing();
        }
    }
    
        5
  •  10
  •   AllenM    14 年前

    PassEventArgsToCommand=“真” 需要如上所述。

    (归功于Laurent Bugnion http://blog.galasoft.ch/archive/2009/10/18/clean-shutdown-in-silverlight-and-wpf-applications.aspx )

       ... MainWindow Xaml
       ...
       WindowStyle="ThreeDBorderWindow" 
        WindowStartupLocation="Manual">
    
    
    
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <cmd:EventToCommand Command="{Binding WindowClosingCommand}" PassEventArgsToCommand="True" />
        </i:EventTrigger>
    </i:Interaction.Triggers> 
    

    在视图模型中:

    ///<summary>
    ///  public RelayCommand<CancelEventArgs> WindowClosingCommand
    ///</summary>
    public RelayCommand<CancelEventArgs> WindowClosingCommand { get; private set; }
     ...
     ...
     ...
            // Window Closing
            WindowClosingCommand = new RelayCommand<CancelEventArgs>((args) =>
                                                                          {
                                                                              ShutdownService.MainWindowClosing(args);
                                                                          },
                                                                          (args) => CanShutdown);
    

    在停机服务中

        /// <summary>
        ///   ask the application to shutdown
        /// </summary>
        public static void MainWindowClosing(CancelEventArgs e)
        {
            e.Cancel = true;  /// CANCEL THE CLOSE - let the shutdown service decide what to do with the shutdown request
            RequestShutdown();
        }
    

    RequestShutdown看起来像下面这样,但是basicallyRequestShutdown或它的名称决定了是否关闭应用程序(这将愉快地关闭窗口):

    ...
    ...
    ...
        /// <summary>
        ///   ask the application to shutdown
        /// </summary>
        public static void RequestShutdown()
        {
    
            // Unless one of the listeners aborted the shutdown, we proceed.  If they abort the shutdown, they are responsible for restarting it too.
    
            var shouldAbortShutdown = false;
            Logger.InfoFormat("Application starting shutdown at {0}...", DateTime.Now);
            var msg = new NotificationMessageAction<bool>(
                Notifications.ConfirmShutdown,
                shouldAbort => shouldAbortShutdown |= shouldAbort);
    
            // recipients should answer either true or false with msg.execute(true) etc.
    
            Messenger.Default.Send(msg, Notifications.ConfirmShutdown);
    
            if (!shouldAbortShutdown)
            {
                // This time it is for real
                Messenger.Default.Send(new NotificationMessage(Notifications.NotifyShutdown),
                                       Notifications.NotifyShutdown);
                Logger.InfoFormat("Application has shutdown at {0}", DateTime.Now);
                Application.Current.Shutdown();
            }
            else
                Logger.InfoFormat("Application shutdown aborted at {0}", DateTime.Now);
        }
        }
    
        6
  •  8
  •   Chris    11 年前

    在窗口或usercontrol等顶部的定义中,定义命名空间:

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    

    就在这个定义之下:

    <i:Interaction.Triggers>
            <i:EventTrigger EventName="Closing">
                <i:InvokeCommandAction Command="{Binding WindowClosing}" CommandParameter="{Binding}" />
            </i:EventTrigger>
    </i:Interaction.Triggers>
    

    public ICommand WindowClosing { get; private set; }
    

    在viewmodel构造函数中附加delegatecommand:

    this.WindowClosing = new DelegateCommand<object>(this.OnWindowClosing);
    

    最后,在关闭控件/窗口/任何东西时要访问的代码:

    private void OnWindowClosing(object obj)
            {
                //put code here
            }
    
        7
  •  4
  •   Tatranskymedved    8 年前

    我很想在您的应用程序.xaml.cs允许您决定是否关闭应用程序的文件。

    例如,您可以在应用程序.xaml.cs文件:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        // Create the ViewModel to attach the window to
        MainWindow window = new MainWindow();
        var viewModel = new MainWindowViewModel();
    
        // Create the handler that will allow the window to close when the viewModel asks.
        EventHandler handler = null;
        handler = delegate
        {
            //***Code here to decide on closing the application****
            //***returns resultClose which is true if we want to close***
            if(resultClose == true)
            {
                viewModel.RequestClose -= handler;
                window.Close();
            }
        }
        viewModel.RequestClose += handler;
    
        window.DataContaxt = viewModel;
    
        window.Show();
    
    }
    

    然后在主WindowViewModel代码中,可以有以下内容:

    #region Fields
    RelayCommand closeCommand;
    #endregion
    
    #region CloseCommand
    /// <summary>
    /// Returns the command that, when invoked, attempts
    /// to remove this workspace from the user interface.
    /// </summary>
    public ICommand CloseCommand
    {
        get
        {
            if (closeCommand == null)
                closeCommand = new RelayCommand(param => this.OnRequestClose());
    
            return closeCommand;
        }
    }
    #endregion // CloseCommand
    
    #region RequestClose [event]
    
    /// <summary>
    /// Raised when this workspace should be removed from the UI.
    /// </summary>
    public event EventHandler RequestClose;
    
    /// <summary>
    /// If requested to close and a RequestClose delegate has been set then call it.
    /// </summary>
    void OnRequestClose()
    {
        EventHandler handler = this.RequestClose;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
    
    #endregion // RequestClose [event]
    
        8
  •  1
  •   Echtelion    15 年前

    基本上,窗口事件不能分配给MVVM。一般情况下,关闭按钮会显示一个对话框,询问用户“保存:是/否/取消”,这可能是MVVM无法实现的。

    因此,如果CanExecute()调用为true,或者在OnClosed事件中,调用模型.关闭.执行()

        9
  •  1
  •   Brian Ortiz    15 年前

    我没有做过很多测试,但它似乎起作用了。我想到的是:

    namespace OrtzIRC.WPF
    {
        using System;
        using System.Windows;
        using OrtzIRC.WPF.ViewModels;
    
        /// <summary>
        /// Interaction logic for App.xaml
        /// </summary>
        public partial class App : Application
        {
            private MainViewModel viewModel = new MainViewModel();
            private MainWindow window = new MainWindow();
    
            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);
    
                viewModel.RequestClose += ViewModelRequestClose;
    
                window.DataContext = viewModel;
                window.Closing += Window_Closing;
                window.Show();
            }
    
            private void ViewModelRequestClose(object sender, EventArgs e)
            {
                viewModel.RequestClose -= ViewModelRequestClose;
                window.Close();
            }
    
            private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
            {
                window.Closing -= Window_Closing;
                viewModel.RequestClose -= ViewModelRequestClose; //Otherwise Close gets called again
                viewModel.CloseCommand.Execute(null);
            }
        }
    }
    
        10
  •  1
  •   wonea Ilya Smagin    9 年前

    为此,我们使用attachedCommand行为。您可以将任何事件附加到视图模型上的命令,以避免任何代码隐藏。

    我们在整个解决方案中都使用它,几乎没有代码隐藏

    http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/

        11
  •  1
  •   rmojab63    9 年前

    出口 视图模型中的命令:

    ICommand _exitCommand;
    public ICommand ExitCommand
    {
        get
        {
            if (_exitCommand == null)
                _exitCommand = new RelayCommand<object>(call => OnExit());
            return _exitCommand;
        }
    }
    
    void OnExit()
    {
         var msg = new NotificationMessageAction<object>(this, "ExitApplication", (o) =>{});
         Messenger.Default.Send(msg);
    }
    

    Messenger.Default.Register<NotificationMessageAction<object>>(this, (m) => if (m.Notification == "ExitApplication")
    {
         Application.Current.Shutdown();
    });
    

    另一方面,我处理 Closing MainWindow ,使用ViewModel的实例:

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    { 
        if (((ViewModel.MainViewModel)DataContext).CancelBeforeClose())
            e.Cancel = true;
    }
    

    CancelBeforeClose 检查视图模型的当前状态,如果应停止关闭,则返回true。

        12
  •  -2
  •   Bhargav Rao rlgjr    9 年前
    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            MessageBox.Show("closing");
        }