You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF开发:如何让一个按钮先后执行两个命令?

当然可以实现一个按钮先后执行两个命令!针对你这个WPF MVVM游戏开发的场景,我给你几个适配现有代码结构的可行方案,你可以根据自己的偏好选择:


方案1:在谜题校验命令中直接触发导航(最直接的MVVM方式)

既然你的核心需求是校验通过后才导航,可以直接把导航逻辑整合到SolvedCheck命令的执行流程里。只需要让PuzzleViewModel能拿到MainViewModel的引用,就能调用它的NavigateCommand。

步骤1:修改PuzzleViewModel,注入MainViewModel引用

public class PuzzleViewModel : ViewModelBase
{
    private readonly MainViewModel _mainViewModel;
    public RelayCommand SolvedCheck { get; }

    // 通过构造函数注入MainViewModel
    public PuzzleViewModel(MainViewModel mainViewModel)
    {
        _mainViewModel = mainViewModel;
        SolvedCheck = new RelayCommand(ExecuteSolvedCheck);
    }

    private void ExecuteSolvedCheck()
    {
        // 执行你的谜题校验逻辑
        bool isPuzzleSolved = CheckIfPuzzleIsCorrect();

        if (isPuzzleSolved)
        {
            // 校验通过,触发导航命令(对应你原来Button1的参数5)
            _mainViewModel.NavigateCommand.Execute(5);
        }
        else
        {
            // 校验失败,给用户提示(比如弹窗或更新UI状态)
            MessageBox.Show("谜题还没解开哦,再试试!");
        }
    }

    // 替换成你实际的谜题校验逻辑
    private bool CheckIfPuzzleIsCorrect()
    {
        // 示例:返回当前谜题是否解开的判断结果
        return true;
    }
}

步骤2:给GameViewModel传递MainViewModel引用

修改MainViewModel中GameViewModel的初始化代码,把自身作为参数传入:

private readonly Dictionary<int, Lazy<IPage>> pages = new Dictionary<int, Lazy<IPage>>
{
    [1] = new Lazy<IPage>(() => new IntroViewModel()),
    [2] = new Lazy<IPage>(() => new ChoosePuzzleViewModel()),
    // 传递this(MainViewModel实例)给GameViewModel
    [3] = new Lazy<IPage>(() => new GameViewModel(5, this)),
    [4] = new Lazy<IPage>(() => new GameViewModel(7, this)),
    [5] = new Lazy<IPage>(() => new GameViewModel(9, this)),
    [6] = new Lazy<IPage>(() => new ResultViewModel())
};

然后更新GameViewModel的构造函数,接收并传递MainViewModel给VMPuzzle:

public class GameViewModel : ViewModelBase, IPage
{
    public PuzzleViewModel VMPuzzle { get; }

    public GameViewModel(int puzzleSize, MainViewModel mainViewModel)
    {
        VMPuzzle = new PuzzleViewModel(mainViewModel);
        // 其他初始化逻辑
    }
}

步骤3:简化Button的绑定

现在你的按钮只需要绑定SolvedCheck命令就行,不用再处理两个命令:

<Button Name="SolveButton" Content="Check" Height="80" Width="100" 
        Command="{Binding VMPuzzle.SolvedCheck}" />

方案2:自定义复合命令(适合需要复用命令组合的场景)

如果以后需要频繁组合多个命令,可以自己实现一个条件复合命令,先执行校验命令,再根据校验结果决定是否执行导航命令。

步骤1:实现复合命令类

public class ConditionalCompositeCommand : ICommand
{
    private readonly ICommand _firstCommand;
    private readonly Func<bool> _shouldExecuteSecond;
    private readonly ICommand _secondCommand;

    public ConditionalCompositeCommand(ICommand firstCommand, Func<bool> shouldExecuteSecond, ICommand secondCommand)
    {
        _firstCommand = firstCommand;
        _shouldExecuteSecond = shouldExecuteSecond;
        _secondCommand = secondCommand;
    }

    public event EventHandler? CanExecuteChanged;

    public bool CanExecute(object? parameter)
    {
        return _firstCommand.CanExecute(parameter) && (_secondCommand?.CanExecute(parameter) ?? true);
    }

    public void Execute(object? parameter)
    {
        // 先执行第一个命令(校验)
        _firstCommand.Execute(parameter);
        // 满足条件则执行第二个命令(导航)
        if (_shouldExecuteSecond())
        {
            _secondCommand.Execute(parameter);
        }
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

步骤2:在UserControl的ViewModel中创建复合命令

假设你的UserControl绑定的ViewModel是GameViewModel,在其中创建复合命令:

public class GameViewModel : ViewModelBase, IPage
{
    public PuzzleViewModel VMPuzzle { get; }
    public ICommand CheckAndNavigateCommand { get; }

    public GameViewModel(int puzzleSize, ICommand mainNavigateCommand)
    {
        VMPuzzle = new PuzzleViewModel();
        // 创建复合命令:先执行校验,再根据IsPuzzleSolved决定是否导航
        CheckAndNavigateCommand = new ConditionalCompositeCommand(
            VMPuzzle.SolvedCheck,
            () => VMPuzzle.IsPuzzleSolved, // 需要PuzzleViewModel暴露一个校验结果属性
            new RelayCommand(() => mainNavigateCommand.Execute(5))
        );
    }
}

步骤3:绑定复合命令到按钮

<Button Name="SolveButton" Content="Check" Height="80" Width="100" 
        Command="{Binding CheckAndNavigateCommand}" />

方案3:使用WPF行为(适合不想修改太多ViewModel的场景)

如果不想改动ViewModel的结构,可以用WPF的行为库(需要安装Microsoft.Xaml.Behaviors.Wpf NuGet包),给按钮添加多个触发动作,按顺序执行命令。

步骤1:实现条件执行行为类

public class InvokeCommandConditionallyAction : TriggerAction<Button>
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(InvokeCommandConditionallyAction));

    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register(nameof(CommandParameter), typeof(object), typeof(InvokeCommandConditionallyAction));

    public static readonly DependencyProperty ConditionProperty =
        DependencyProperty.Register(nameof(Condition), typeof(bool), typeof(InvokeCommandConditionallyAction));

    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }

    public bool Condition
    {
        get => (bool)GetValue(ConditionProperty);
        set => SetValue(ConditionProperty, value);
    }

    protected override void Invoke(object parameter)
    {
        if (Condition && Command != null && Command.CanExecute(CommandParameter))
        {
            Command.Execute(CommandParameter);
        }
    }
}

步骤2:在XAML中配置行为

<!-- 先引用行为命名空间 -->
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:你的项目命名空间"

<Button Name="SolveButton" Content="Check" Height="80" Width="100">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <!-- 第一步:执行谜题校验命令 -->
            <i:InvokeCommandAction Command="{Binding VMPuzzle.SolvedCheck}" />
            <!-- 第二步:如果校验通过,执行导航命令 -->
            <local:InvokeCommandConditionallyAction 
                Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"
                CommandParameter="5"
                Condition="{Binding VMPuzzle.IsPuzzleSolved}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

内容的提问来源于stack exchange,提问作者Zero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:32:49