如何将关闭命令绑定到按钮

67

最简单的方法是实现ButtonClick事件处理程序并调用Window.Close()方法,但如何通过Command绑定完成此操作?

7个回答

82

只需要一点点的XAML即可...

<Window x:Class="WCSamples.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Close"
                        Executed="CloseCommandHandler"/>
    </Window.CommandBindings>
    <StackPanel Name="MainStackPanel">
        <Button Command="ApplicationCommands.Close" 
                Content="Close Window" />
    </StackPanel>
</Window>

还有一点点C#代码...

private void CloseCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
    this.Close();
}

(改编自此MSDN文章)


3
呼,我原以为可以不用C#代码完成这个任务。现在看来最好还是采用老式的定义按钮点击行为的方法。 - iLemming
4
使用命令的好处在于它们不受特定用户界面控件的约束;你可以使用同一命令通过按钮、菜单选项或键盘快捷键关闭应用程序。虽然此功能并不适用于“关闭”操作,但命令还提供了一种方法,即在无法触发时自动禁用相应的UI控件,就像当剪贴板中没有文本时,“粘贴”功能被禁用一样。 - Nicholas Armstrong
3
使用内置的 ApplicationCommands.Close 命令,点赞 +1。 - Chris Kerekes

70

实际上,没有使用C#代码也是可以做到的。 关键是要使用交互:

<Button Content="Close">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

为了使其正常工作,只需将窗口的x:Name设置为"window",然后添加这两个命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

这需要您将Expression Blend SDK DLL添加到您的项目中,具体来说是Microsoft.Expression.Interactions

如果您没有Blend,则可以从此处下载SDK。


顺便问一下,使用这个方法能传递任何参数吗? - Dean Kuga
7
直到我将Microsoft.Expression.Interactions添加为引用,它才能识别ei:部分。 - Maslow
1
我简直不敢相信我花了多长时间才找到这个答案。大多数解决方案都过于复杂,只要你不介意使用混合 SDK 文件就可以了。 - Wes
你可以使用以下代码替换ElementName:TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" - Welcor

57

我认为在真实世界的场景中,一个简单的点击处理程序可能比过度复杂的基于命令的系统更好,但你可以像这样做:

使用这篇文章中的RelayCommand http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

public class MyCommands
{
    public static readonly ICommand CloseCommand =
        new RelayCommand( o => ((Window)o).Close() );
}
<Button Content="Close Window"
        Command="{X:Static local:MyCommands.CloseCommand}"
        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, 
                           AncestorType={x:Type Window}}}"/>

3
如果您不想使用RelayCommand,则可以编写自定义命令:class WindowCloseCmd : ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged { add { } remove { } } public void Execute(object wnd) { if (wnd is Window) ((Window)wnd).Close(); } } public static readonly ICommand WindowCloseCommand = new WindowCloseCmd(); - Peter

53

如果使用 Window.ShowDialog() 显示窗口:

我知道的最简单的解决方法是将关闭按钮的 IsCancel 属性设置为 true:

<Button Content="Close" IsCancel="True" />

不需要绑定,WPF会自动完成这项工作!

这些属性提供了一种简单的方式来表示对话框上的“确定”和“取消”按钮。它还将ESC键绑定到该按钮。

参考:MSDN Button.IsCancel property


如果你正在使用模态窗口/对话框,那么IsCancel="True"会是更好的选择,因为它所做的只是调用窗口上的取消操作。由于普通窗口不响应取消操作,所以它无法起作用。 - kiran

5

.NET 4.5 SystemCommands类可以解决问题(.NET 4.0用户可以使用WPF Shell Extension google - Microsoft.Windows.Shell或Nicholas Solution)。

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" 
                        CanExecute="CloseWindow_CanExec" 
                        Executed="CloseWindow_Exec" />
    </Window.CommandBindings>
    <!-- Binding Close Command to the button control -->
    <Button ToolTip="Close Window" Content="Close" Command="{x:Static SystemCommands.CloseWindowCommand}"/>

在代码后台,您可以像这样实现处理程序:
    private void CloseWindow_CanExec(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void CloseWindow_Exec(object sender, ExecutedRoutedEventArgs e)
    {
        SystemCommands.CloseWindow(this);
    }

为什么要使用SystemCommands.CloseWindow(this);而不是this.Close()(或somewindow.Close())? - StayOnTarget

3

起初,我也有点困惑这个如何工作,所以我想发布一个更好的解释,说明实际上正在发生什么。

根据我的研究,处理这种情况的最佳方式是使用命令绑定(Command Bindings)。发生的情况是一个“消息”被广播到程序中的所有内容。所以你必须使用CommandBinding。这本质上是说“当你听到这个消息时,做这个事情”。

因此,在该问题中,用户正在试图关闭窗口。我们需要做的第一件事是设置我们的函数,这些函数将在广播SystemCommand.CloseWindowCommand时调用。可选地,您可以分配一个函数来确定是否应执行命令。例如,关闭一个表单并检查用户是否已保存。

MainWindow.xaml.cs (或其他代码后台)

void CloseApp( object target, ExecutedRoutedEventArgs e ) {
    /*** Code to check for State before Closing ***/
    this.Close();
}

void CloseAppCanExecute( object sender, CanExecuteRoutedEventArgs e ) {
    /*** Logic to Determine if it is safe to Close the Window ***/
    e.CanExecute = true;
}

现在我们需要设置“连接”SystemCommands.CloseWindowCommandCloseApp以及CloseAppCanExecute之间的关系。

MainWindow.xaml(或任何实现CommandBindings的内容)

<Window.CommandBindings>
    <CommandBinding Command="SystemCommands.CloseWindowCommand"
                    Executed="CloseApp"
                    CanExecute="CloseAppCanExecute"/>
</Window.CommandBindings>

如果您知道命令始终可以执行,可以省略CanExecute。根据应用程序,Save可能是一个很好的例子。以下是一个示例:

<Window.CommandBindings>
    <CommandBinding Command="SystemCommands.CloseWindowCommand"
                    Executed="CloseApp"/>
</Window.CommandBindings>

最后,您需要告诉UIElement发送CloseWindowCommand。
<Button Command="SystemCommands.CloseWindowCommand">

这其实是一件非常简单的事情,只需设置命令与实际执行函数之间的链接,然后告诉控制器向程序中的其他部分发送命令,说“好了,每个人都运行CloseWindowCommand的函数”。

这实际上是一种非常好的处理方式,因为您可以在不使用包装器的情况下在整个程序中重复使用已执行的函数,就像在WinForms中使用ClickEvent并在事件函数中调用函数一样:

protected override void OnClick(EventArgs e){
    /*** Function to Execute ***/
}

在WPF中,您将函数附加到命令,并告诉UI元素执行附加到命令的函数。希望这可以澄清事情...

0

我发现可行的一个选项是将此函数设置为行为。

该行为:

    public class WindowCloseBehavior : Behavior<Window>
{
    public bool Close
    {
        get { return (bool) GetValue(CloseTriggerProperty); }
        set { SetValue(CloseTriggerProperty, value); }
    }

    public static readonly DependencyProperty CloseTriggerProperty =
        DependencyProperty.Register("Close", typeof(bool), typeof(WindowCloseBehavior),
            new PropertyMetadata(false, OnCloseTriggerChanged));

    private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as WindowCloseBehavior;

        if (behavior != null)
        {
            behavior.OnCloseTriggerChanged();
        }
    }

    private void OnCloseTriggerChanged()
    {
        // when closetrigger is true, close the window
        if (this.Close)
        {
            this.AssociatedObject.Close();
        }
    }
}

在XAML窗口中,您需要设置对其的引用,并将Behavior的Close属性绑定到ViewModel上的布尔型“Close”属性。
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Behaviors>
    <behavior:WindowCloseBehavior Close="{Binding Close}" />
</i:Interaction.Behaviors>

所以,从视图中分配一个ICommand来更改绑定到行为的Close属性的ViewModel上的Close属性。当触发PropertyChanged事件时,行为会触发OnCloseTriggerChanged事件并关闭关联对象...即窗口。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接