在 Checkbox.Checked 或 Unchecked 上执行命令

发布于 2024-10-31 01:36:48 字数 82 浏览 3 评论 0 原文

我的窗口上有一个复选框控件。我想执行一个命令来调用关联视图模型中的方法。我还需要复选框的值。我似乎找不到将命令与复选框关联起来的方法。有人这样做过吗?

I have a checkbox control on a window. I'd like to execute a command that will call a method in the associated view model. I'll also need the value of the checkbox as well. I cannot seem to find a way to associate a command with a checkbox. Has anyone done this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

暖伴 2024-11-07 01:36:48
<CheckBox Content="CheckBox"
          Command="{Binding YourCommand}"
          CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}" />
<CheckBox Content="CheckBox"
          Command="{Binding YourCommand}"
          CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}" />
无风消散 2024-11-07 01:36:48

如果您使用 MVVM,则可以使用如下事件触发器:

<CheckBox IsChecked="{Binding ServiceOrderItemTask.IsCompleted, Mode=TwoWay}" Content="{Binding ServiceOption.Name}">

    <i:Interaction.Triggers>
          <i:EventTrigger EventName="Checked">
                 <i:InvokeCommandAction Command="{Binding DataContext.IsCompletedCheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type t:RadGridView}}}" CommandParameter="{Binding}"/>
           </i:EventTrigger>

           <i:EventTrigger EventName="Unchecked">
                 <i:InvokeCommandAction Command="{Binding DataContext.IsCompletedUncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type t:RadGridView}}}" CommandParameter="{Binding}"/>
           </i:EventTrigger>
    </i:Interaction.Triggers>

If you use MVVM, you can use event triggers like this:

<CheckBox IsChecked="{Binding ServiceOrderItemTask.IsCompleted, Mode=TwoWay}" Content="{Binding ServiceOption.Name}">

    <i:Interaction.Triggers>
          <i:EventTrigger EventName="Checked">
                 <i:InvokeCommandAction Command="{Binding DataContext.IsCompletedCheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type t:RadGridView}}}" CommandParameter="{Binding}"/>
           </i:EventTrigger>

           <i:EventTrigger EventName="Unchecked">
                 <i:InvokeCommandAction Command="{Binding DataContext.IsCompletedUncheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type t:RadGridView}}}" CommandParameter="{Binding}"/>
           </i:EventTrigger>
    </i:Interaction.Triggers>

拥抱影子 2024-11-07 01:36:48

这将满足您的要求 -

<CheckBox CommandParameter="{Binding}"
          Command="{Binding DataContext.YourCommand,
          RelativeSource={RelativeSource FindAncestor,
                           AncestorType={x:Type UserControl}}}"
          Content="{Binding Path=Name}">

This will work what you requires -

<CheckBox CommandParameter="{Binding}"
          Command="{Binding DataContext.YourCommand,
          RelativeSource={RelativeSource FindAncestor,
                           AncestorType={x:Type UserControl}}}"
          Content="{Binding Path=Name}">
爱格式化 2024-11-07 01:36:48
  • System.Windows.Interactivity 添加到您的项目引用中。
  • xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 添加到您的 XAML 命名空间。
<CheckBox IsChecked="{Binding SomeBoolProperty, Mode=OneWay}" Content="Check Meee!">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Checked">
            <i:InvokeCommandAction Command="{Binding MyOnCheckedCommand}"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="Unchecked">
            <i:InvokeCommandAction Command="{Binding MyOnUncheckedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</CheckBox>

我在 ViewModel 上实现 INotifyPropertyChanged 如下:

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

我的 ViewModel 的 SomeBoolProperty 看起来像这样:

private bool _SomeBoolProperty = false;
public bool SomeBoolProperty { 
    get => _SomeBoolProperty;
    set { 
        _SomeBoolProperty = value; 
        OnPropertyChanged(nameof(SomeBoolProperty)); 
    } 
}

我使用 RelayCommand 作为我的命令实现
https://stackoverflow.com/a/22286816/336753

我的 ViewModel 上的命令如下所示:

public ICommand MyOnCheckedCommand { get; } = new RelayCommand(o => {
    // Do something here.
    SomeBoolProperty = true;
});
public ICommand MyOnUncheckedCommand { get; } = new RelayCommand(o => {
    // Do something else here.
    SomeBoolProperty = false;
});

我遇到了这个问题,试图找到一种方法来重用我的 ViewModel 上已有的两个命令。一个在选中时调用,一个在未选中时调用。我也在一些按钮上使用它们,所以不想添加额外的参数化命令。人们在这里询问有关 ViewModel 实现的问题,因此添加此答案来完成 Igor_S 中的答案。希望有帮助。

  • Add System.Windows.Interactivity to your project references.
  • Add xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" to your XAML namespaces.
<CheckBox IsChecked="{Binding SomeBoolProperty, Mode=OneWay}" Content="Check Meee!">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Checked">
            <i:InvokeCommandAction Command="{Binding MyOnCheckedCommand}"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="Unchecked">
            <i:InvokeCommandAction Command="{Binding MyOnUncheckedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</CheckBox>

I implement INotifyPropertyChanged on my ViewModel as follows:

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

The SomeBoolProperty of my ViewModel then looks like this:

private bool _SomeBoolProperty = false;
public bool SomeBoolProperty { 
    get => _SomeBoolProperty;
    set { 
        _SomeBoolProperty = value; 
        OnPropertyChanged(nameof(SomeBoolProperty)); 
    } 
}

I use RelayCommand as my command implementation from here
https://stackoverflow.com/a/22286816/336753.

The commands on my ViewModel then look like this:

public ICommand MyOnCheckedCommand { get; } = new RelayCommand(o => {
    // Do something here.
    SomeBoolProperty = true;
});
public ICommand MyOnUncheckedCommand { get; } = new RelayCommand(o => {
    // Do something else here.
    SomeBoolProperty = false;
});

I got to this question trying to find a way to reuse two commands I already had on my ViewModel. One called when checked and one when unchecked. I use them on some buttons too so did not want to add an extra parametrized command. People were asking here about ViewModel implementation so adding this answer to complete the one from Igor_S. Hope it helps.

旧城空念 2024-11-07 01:36:48

以下是我根据 Microsoft 文档和其他 SO 问题尝试过的内容:

这些都没有达到我想要的效果,即与您类似,根据复选框属性更改调用方法/命令,因为这些要么混乱又费解或者违反了 MVVM 原则,因为它们仅限于视图而不是视图模型。

类似于对一个单独问题的这个答案,以及这个答案,我最终使用了EventToCommandBehavior 来自 .NET MAUI 社区工具包的行为。我不知道这是否适用于 WPF,抱歉。

这是我针对此示例的简化代码。

MainPage.xaml

<ContentPage
        x:Class="MyProject.Views.MainPage"
        xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
        xmlns:viewmodel="clr-namespace:MyProject.ViewModels"
        x:DataType="viewmodel:MainPageViewModel">
 
        <CheckBox x:Name="WholeNameCheckbox"
              IsChecked="{Binding IsExactMatch}"
              HorizontalOptions="Center">
              <CheckBox.Behaviors>
                    <toolkit:EventToCommandBehavior
                               EventName="CheckedChanged"
                               Command="{Binding ExactMatchCheckedCommand}"
                               CommandParameter="{Binding IsChecked, Source={x:Reference WholeNameCheckbox}}"/>
             </CheckBox.Behaviors>
        </CheckBox>
</ContentPage>

MainPage.xaml.cs

namespace MyProject.Views;

public partial class MainPage : ContentPage
{
    public MainPage(MainPageViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

MainPageViewModel.cs

using MyProject.Services;
using MvvmHelpers;   // For [RelayCommand]

namespace MyProject.ViewModels;

public partial class MainPageViewModel : BaseViewModel 
{
  // ObservableProperty gives the variable a public property and
  // all the NotifyChanges interfaces and gives the properties
  // OnPropertyChanged() methods.
  [ObservableProperty]
  bool _returnsPartialMatch;

  [ObservableProperty]
  bool _returnsPartialMatchWithSpace;

  [ObservableProperty]
  public bool _isExactMatch;

  [RelayCommand]
  async Task ExactMatchCheckedAsync(bool isChecked_p)
  {
       // Offload processing to thread pool.
       await Task.Run(() =>
       {
           // Do stuff in your view model using your properties or whatever
       }
  }
}

按预期工作 - 当选中“精确匹配”复选框时, IsExactMatch 属性为 True,无论哪种方式,当选中状态更改时,它都会调用视图模型中的异步方法,并将其 IsChecked 属性值传递给该方法。由于此命令是在 IsExactMatch 属性更改后调用的,因此该值更改或需要验证它的时间并不奇怪。

Here's stuff I tried according to Microsoft docs and other SO questions:

None of these did what I wanted which was, similar to you, to call a method/command based on the Checkbox property changing, because these were either messy and convoluted or broke MVVM principles because they were limited to the View and not the View Model.

Similar to this SO answer to a separate question, and this SO answer, I ended up using the EventToCommandBehavior behaviour from the .NET MAUI Community Toolkit. I don't know about how this may or may not work with WPF, sorry.

Here's my simplified code for this example.

MainPage.xaml:

<ContentPage
        x:Class="MyProject.Views.MainPage"
        xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
        xmlns:viewmodel="clr-namespace:MyProject.ViewModels"
        x:DataType="viewmodel:MainPageViewModel">
 
        <CheckBox x:Name="WholeNameCheckbox"
              IsChecked="{Binding IsExactMatch}"
              HorizontalOptions="Center">
              <CheckBox.Behaviors>
                    <toolkit:EventToCommandBehavior
                               EventName="CheckedChanged"
                               Command="{Binding ExactMatchCheckedCommand}"
                               CommandParameter="{Binding IsChecked, Source={x:Reference WholeNameCheckbox}}"/>
             </CheckBox.Behaviors>
        </CheckBox>
</ContentPage>

MainPage.xaml.cs:

namespace MyProject.Views;

public partial class MainPage : ContentPage
{
    public MainPage(MainPageViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}

MainPageViewModel.cs:

using MyProject.Services;
using MvvmHelpers;   // For [RelayCommand]

namespace MyProject.ViewModels;

public partial class MainPageViewModel : BaseViewModel 
{
  // ObservableProperty gives the variable a public property and
  // all the NotifyChanges interfaces and gives the properties
  // OnPropertyChanged() methods.
  [ObservableProperty]
  bool _returnsPartialMatch;

  [ObservableProperty]
  bool _returnsPartialMatchWithSpace;

  [ObservableProperty]
  public bool _isExactMatch;

  [RelayCommand]
  async Task ExactMatchCheckedAsync(bool isChecked_p)
  {
       // Offload processing to thread pool.
       await Task.Run(() =>
       {
           // Do stuff in your view model using your properties or whatever
       }
  }
}

Works as expected - when the Exact Match checkbox is checked, IsExactMatch property is True, and either way, when the checked state is changed it calls the asynchronous method in the view model and passes its IsChecked property value to the method. Since this command is called after the IsExactMatch property has been changed, there's no weirdness with timing on that value changing or needing to validate it.

不语却知心 2024-11-07 01:36:48

我迟到了......我使用了 Rohit Vats 的答案并想出了这段代码。

该示例是一个工作代码摘录,它只是为了帮助理解各个方面。它是一个图钉,可以是活动的也可以是非活动的,并且它使用 DelegateCommand。您也可以使用 RelayCommand 或任何其他类似的类来完成相同的工作。

命令:

using System.Windows.Input;

namespace HQ.Wpf.Util.Command
{
    public class StandardCommand
    {
        public static RoutedUICommand PinPropertyGrid = new RoutedUICommand("Pin property grid", "PinPropertyGrid", typeof(StandardCommand));

Xaml:

                            <CheckBox HorizontalAlignment="Right" 
                                      VerticalAlignment="Top"
                                      Margin="2,0,3,0" 
                                      Command="{Binding CommandPinPropertyGrid}"
                                      CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}">
                                <CheckBox.Template>
                                    <ControlTemplate TargetType="{x:Type CheckBox}">
                                        <Grid>
                                            <Image x:Name="ImagePushpin" Width="16" Height="16" Source="pack://application:,,,/WpfUtil;component/Images/PushpinUnpinned16x16.png" />
                                        </Grid>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter TargetName="ImagePushpin" Property="Source" Value="pack://application:,,,/WpfUtil;component/Images/PushpinPinned16x16.png" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </CheckBox.Template>
                            </CheckBox>

模型:

public MainWindowViewModel()
{
    CommandPinPropertyGrid = new DelegateCommand<bool>(PinPropertyGrid);

...

// ******************************************************************
public DelegateCommand<bool> CommandPinPropertyGrid { get; private set; }

public void PinPropertyGrid(bool pinned)
{
    this.IsPropertyGridPinned = pinned;
}

DelegateCommand:

using System;
using System.Windows.Input;

namespace HQ.Wpf.Util.Command
{

    /// <summary>
    /// Represents a command that forwards the <c>Execute</c> and <c>CanExecute</c> calls to specified delegates.
    /// </summary>
    public class DelegateCommand<T> : ICommand
    {

        private readonly Action<T> _executeCallback;
        private readonly Predicate<T> _canExecuteCallback;

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // OBJECT
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateCommand<T>"/> class.
        /// </summary>
        /// <param name="executeCallback">The execute callback delegate.</param>
        public DelegateCommand(Action<T> executeCallback)
            : this(executeCallback, null)
        {
            // No-op
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateCommand<T>"/> class.
        /// </summary>
        /// <param name="executeCallback">The execute callback delegate.</param>
        /// <param name="canExecuteCallback">The can execute callback delegate.</param>
        public DelegateCommand(Action<T> executeCallback, Predicate<T> canExecuteCallback)
        {
            if (executeCallback == null)
                throw new ArgumentNullException("executeCallback");

            this._executeCallback = executeCallback;
            this._canExecuteCallback = canExecuteCallback;
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // INTERFACE IMPLEMENTATION
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        #region ICommand Members

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to <see langword="null"/>.</param>
        /// <returns>
        /// <c>true</c> if this command can be executed; otherwise, <c>false</c>.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            return (this._canExecuteCallback == null) ? true : this._canExecuteCallback((T)parameter);
        }

        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (this._canExecuteCallback != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (this._canExecuteCallback != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to <see langword="null"/>.</param>
        public void Execute(object parameter)
        {
            this._executeCallback((T)parameter);
        }

        #endregion // ICommand Members

    }
}

I'm late... I used Rohit Vats answer and came up with this code.

The example is a working code extract and it is only here to help to understand every aspects. It is a pushpin that could be either active or inactive and it use a DelegateCommand. You could probably also use a RelayCommand or any other similar class to do the same job.

Command:

using System.Windows.Input;

namespace HQ.Wpf.Util.Command
{
    public class StandardCommand
    {
        public static RoutedUICommand PinPropertyGrid = new RoutedUICommand("Pin property grid", "PinPropertyGrid", typeof(StandardCommand));

Xaml:

                            <CheckBox HorizontalAlignment="Right" 
                                      VerticalAlignment="Top"
                                      Margin="2,0,3,0" 
                                      Command="{Binding CommandPinPropertyGrid}"
                                      CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}">
                                <CheckBox.Template>
                                    <ControlTemplate TargetType="{x:Type CheckBox}">
                                        <Grid>
                                            <Image x:Name="ImagePushpin" Width="16" Height="16" Source="pack://application:,,,/WpfUtil;component/Images/PushpinUnpinned16x16.png" />
                                        </Grid>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter TargetName="ImagePushpin" Property="Source" Value="pack://application:,,,/WpfUtil;component/Images/PushpinPinned16x16.png" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </CheckBox.Template>
                            </CheckBox>

Model:

public MainWindowViewModel()
{
    CommandPinPropertyGrid = new DelegateCommand<bool>(PinPropertyGrid);

...

// ******************************************************************
public DelegateCommand<bool> CommandPinPropertyGrid { get; private set; }

public void PinPropertyGrid(bool pinned)
{
    this.IsPropertyGridPinned = pinned;
}

DelegateCommand:

using System;
using System.Windows.Input;

namespace HQ.Wpf.Util.Command
{

    /// <summary>
    /// Represents a command that forwards the <c>Execute</c> and <c>CanExecute</c> calls to specified delegates.
    /// </summary>
    public class DelegateCommand<T> : ICommand
    {

        private readonly Action<T> _executeCallback;
        private readonly Predicate<T> _canExecuteCallback;

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // OBJECT
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateCommand<T>"/> class.
        /// </summary>
        /// <param name="executeCallback">The execute callback delegate.</param>
        public DelegateCommand(Action<T> executeCallback)
            : this(executeCallback, null)
        {
            // No-op
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateCommand<T>"/> class.
        /// </summary>
        /// <param name="executeCallback">The execute callback delegate.</param>
        /// <param name="canExecuteCallback">The can execute callback delegate.</param>
        public DelegateCommand(Action<T> executeCallback, Predicate<T> canExecuteCallback)
        {
            if (executeCallback == null)
                throw new ArgumentNullException("executeCallback");

            this._executeCallback = executeCallback;
            this._canExecuteCallback = canExecuteCallback;
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // INTERFACE IMPLEMENTATION
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        #region ICommand Members

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to <see langword="null"/>.</param>
        /// <returns>
        /// <c>true</c> if this command can be executed; otherwise, <c>false</c>.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            return (this._canExecuteCallback == null) ? true : this._canExecuteCallback((T)parameter);
        }

        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (this._canExecuteCallback != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (this._canExecuteCallback != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to <see langword="null"/>.</param>
        public void Execute(object parameter)
        {
            this._executeCallback((T)parameter);
        }

        #endregion // ICommand Members

    }
}
猫弦 2024-11-07 01:36:48

当您只需要复选框的状态(选中或未选中)时,则不需要参数。使用以下代码时,您可以检测复选框的状态:

CheckBox box = e.OriginalSource as CheckBox;

if(box.IsChecked.Value)
    DoThis();
else
    DoAnotherMethod();

“e”是命令中的 ExecutedRoulatedEventArgs 参数。您需要 box.IsChecked.Value,因为 box.IsChecked 来自类型 bool?。

When you need only the Status of the CheckBox (Checked or Unchecked), then you don't need a Parameter. You can detect the Status of the Checkbox when you use this code:

CheckBox box = e.OriginalSource as CheckBox;

if(box.IsChecked.Value)
    DoThis();
else
    DoAnotherMethod();

"e" is the ExecutedRoutedEventArgs-Parameter in the Command. You Need box.IsChecked.Value, because box.IsChecked is from Type bool?.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文