65.9K
CodeProject 正在变化。 阅读更多。
Home

在 ViewModel 中处理 Window 的 Closed 和 Closing 事件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (21投票s)

2010年4月15日

CPOL

2分钟阅读

viewsIcon

188984

downloadIcon

3340

本文讨论了一个附加行为, 该行为允许您通过 ViewModel 中的命令来处理 View Window 的 Closed 和 Closing 事件

引言

本文的灵感来自 Reed Copsey, Jr. 的 Blend 行为,该行为位于 Expression Code Gallery 上。 Reed 的行为使用了一种巧妙的技术,允许 View-Model 以 MVVM 友好的方式处理 View 的 Closing/Closed 事件。 由于他的代码与 Expression DLL 绑定,我认为编写一个纯 WPF 版本会更好。 虽然在概念上与 Blend 行为相似,但我稍微偏离了概念的实现方式以及它的使用方式。 因此,这不是一个直接的 1 对 1 替换,但您应该能够以大致相同的方式工作,而无需付出太多努力。

代码的想法是允许 Window 的 ClosedClosing 事件通过 View-Model 中的命令来处理,为了实现这一点,我为 Window 对象编写了一个附加行为。

用法

该行为暴露了三个命令

  • Closed - 当 Window.Closed 事件触发时,将执行此命令。
  • 闭运算
    • Execute - 如果 Window.Closing 事件未被取消,则执行此命令。
    • CanExecute - 当 Window.Closing 事件触发时,将调用此命令,让您有机会决定是否要取消。
  • CancelClosing - 当 Window.Closing 事件被取消时,将执行此命令。

以下是如何在 View-Model 中实现这些命令的示例。 请注意,该示例中的 MessageBox.Show 调用仅用于演示它是如何工作的。 您可能希望使用某种依赖注入来避免直接的 UI 代码,而是使用某种 MessageBox 服务(假设您确实想用一个 *Yes*/ *No* 消息框提示用户)。

internal class MainViewModel : ViewModelBase
{
    private ObservableCollection<string> log = new ObservableCollection<string>();

    public ObservableCollection<string> Log
    {
        get { return log; }
    }

    private DelegateCommand exitCommand;

    public ICommand ExitCommand
    {
        get
        {
            if (exitCommand == null)
            {
                exitCommand = new DelegateCommand(Exit);
            }
            return exitCommand;
        }
    }

    private void Exit()
    {
        Application.Current.Shutdown();
    }

    private DelegateCommand closedCommand;

    public ICommand ClosedCommand
    {
        get
        {
            if (closedCommand == null)
            {
                closedCommand = new DelegateCommand(Closed);
            }
            return closedCommand;
        }
    }

    private void Closed()
    {
        log.Add("You won't see this of course! Closed command executed");
        MessageBox.Show("Closed");
    }

    private DelegateCommand closingCommand;

    public ICommand ClosingCommand
    {
        get
        {
            if (closingCommand == null)
            {
                closingCommand = new DelegateCommand(
                    ExecuteClosing, CanExecuteClosing);
            }
            return closingCommand;
        }
    }

    private void ExecuteClosing()
    {
        log.Add("Closing command executed");
        MessageBox.Show("Closing");
    }

    private bool CanExecuteClosing()
    {
        log.Add("Closing command execution check");

        return MessageBox.Show("OK to close?", "Confirm", 
            MessageBoxButton.YesNo) == MessageBoxResult.Yes;
    }

    private DelegateCommand cancelClosingCommand;

    public ICommand CancelClosingCommand
    {
        get
        {
            if (cancelClosingCommand == null)
            {
                cancelClosingCommand = new DelegateCommand(CancelClosing);
            }
            return cancelClosingCommand;
        }
    }

    private void CancelClosing()
    {
        log.Add("CancelClosing command executed");
        MessageBox.Show("CancelClosing");
    }
}

以下是如何在 XAML 中附加命令。

<Window x:Class="WindowClosingDemo.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:nsmvvm="clr-namespace:NS.MVVM"
  nsmvvm:WindowClosingBehavior.Closed="{Binding ClosedCommand}"
  nsmvvm:WindowClosingBehavior.Closing="{Binding ClosingCommand}"
  nsmvvm:WindowClosingBehavior.CancelClosing="{Binding CancelClosingCommand}"
  Title="MainWindow" Height="350" Width="525">

实现

这是附加行为的代码清单。 此列表已重新格式化以适应 600 像素的宽度。

public class WindowClosingBehavior
{
    public static ICommand GetClosed(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(ClosedProperty);
    }

    public static void SetClosed(DependencyObject obj, ICommand value)
    {
        obj.SetValue(ClosedProperty, value);
    }

    public static readonly DependencyProperty ClosedProperty 
        = DependencyProperty.RegisterAttached(
        "Closed", typeof(ICommand), typeof(WindowClosingBehavior),
        new UIPropertyMetadata(new PropertyChangedCallback(ClosedChanged)));

    private static void ClosedChanged(
      DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        Window window = target as Window;
        
        if (window != null)
        {
            if (e.NewValue != null)
            {
                window.Closed += Window_Closed;
            }
            else
            {
                window.Closed -= Window_Closed;
            }
        }
    }

    public static ICommand GetClosing(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(ClosingProperty);
    }

    public static void SetClosing(DependencyObject obj, ICommand value)
    {
        obj.SetValue(ClosingProperty, value);
    }

    public static readonly DependencyProperty ClosingProperty 
        = DependencyProperty.RegisterAttached(
        "Closing", typeof(ICommand), typeof(WindowClosingBehavior),
        new UIPropertyMetadata(new PropertyChangedCallback(ClosingChanged)));

    private static void ClosingChanged(
      DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        Window window = target as Window;

        if (window != null)
        {
            if (e.NewValue != null)
            {
                window.Closing += Window_Closing;
            }
            else
            {
                window.Closing -= Window_Closing;
            }
        }
    }

    public static ICommand GetCancelClosing(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(CancelClosingProperty);
    }

    public static void SetCancelClosing(DependencyObject obj, ICommand value)
    {
        obj.SetValue(CancelClosingProperty, value);
    }

    public static readonly DependencyProperty CancelClosingProperty 
        = DependencyProperty.RegisterAttached(
        "CancelClosing", typeof(ICommand), typeof(WindowClosingBehavior));

    static void Window_Closed(object sender, EventArgs e)
    {
        ICommand closed = GetClosed(sender as Window);
        if (closed != null)
        {
            closed.Execute(null);
        }
    }

    static void Window_Closing(object sender, CancelEventArgs e)
    {
        ICommand closing = GetClosing(sender as Window);
        if (closing != null)
        {
            if (closing.CanExecute(null))
            {
                closing.Execute(null);
            }
            else
            {
                ICommand cancelClosing = GetCancelClosing(sender as Window);
                if (cancelClosing != null)
                {
                    cancelClosing.Execute(null);
                }

                e.Cancel = true;
            }
        }
    }
}

我已突出显示上面代码中的相关部分。 对于 Closed 事件,我们只需执行任何可用命令,因为现在担心取消为时已晚。 对于 Closing 事件,Closing 命令的 CanExecute 用于确定我们是否正在取消。 如果我们没有取消,我们执行 Closing 命令,否则我们执行 CancelClosing 命令(如果可用)。 

就这样。 谢谢。

参考

历史

  • 2010 年 4 月 15 日 - 首次发表文章
© . All rights reserved.