将 ItemsControl 中的 Checkbox Click 事件绑定到 viewmodel

Binding Checkbox Click event inside ItemsControl to viewmodel

我有一个 ItemsControl 可以根据 DigitalFilterSubsystems 创建 CheckBoxes。我想在单击 CheckBox 时触发视图模型上的事件处理程序。在他们那一刻,我的 Click 属性 的 CheckBox 正在抱怨 Checked_Triggered 期待 属性。有人可以解释我做错了什么并给出解决方案吗?

<ItemsControl Grid.Column="1" Grid.Row="0" HorizontalAlignment="Center" ItemsSource="{Binding DigitalFilterSubsystems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>        
    <ItemsControl.ItemTemplate>           
        <DataTemplate>
            <StackPanel Orientation="Vertical">
                <TextBlock HorizontalAlignment="Center" Text="{Binding }" Foreground="Black" FontSize="12"/>
                <CheckBox HorizontalAlignment="Center" Click="{Binding Path=Checked_Triggered, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:SimulatorControlViewModel}}, Mode=TwoWay}"/>   
            </StackPanel>
        </DataTemplate>            
    </ItemsControl.ItemTemplate>
</ItemsControl>

在我的名为 SimulatorControlViewModel 的视图模型中:

public void Checked_Triggered(object sender, RoutedEventArgs e)
{ 
    
}

您不能那样绑定到方法,只能绑定到属性。根据您是否使用某种 MVVM 框架,该框架可能会提供一种解决方案。另一种方法是使用绑定到视图模型中定义的命令 属性。看看这里:

首先,您不能将事件处理程序绑定到XAML中的事件。即使可能,您的绑定也是错误的。您的视图模型设置为控件的 DataContext,但 RelativeSource 用于引用 控件 。您要做的是将父控件的类型指定为 AncestorType,它具有所需的数据上下文集,并指定 属性 路径,如 DataContext.YourPropertyToBind,例如:

<CheckBox HorizontalAlignment="Center" IsChecked="{Binding DataContext.MyIsCheckPropertyOnTheViewModel, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>

现在是问题的核心,如果引发 CheckBox Click 事件,则在视图模型中执行操作。有多种选择,具体取决于您的要求。

公开一个属性

如果您 DigitalFilterSubsystems 中的项目是自定义类型,例如Subsystem,你可以公开一个 属性 IsChecked(或者给它一个更有意义的名字来表示选中的意思)。在属性的setter中,调用一个作用于属性的当前值的方法。

public class Subsystem : INotifyPropertyChanged
{
   private bool _isChecked;

   public bool IsChecked
   {
      get => _isChecked;
      set
      {
         if (_isChecked == value)
            return;

         _isChecked = value;
         OnPropertyChanged(nameof(IsChecked));
         OnCheckedChanged();
      }
   }

   private void OnCheckedChanged()
   {
      if (IsChecked)
         // ...do something.
      else
         // ...do something.
   }

   // ...other code.

   public event PropertyChangedEventHandler PropertyChanged;

   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }
}

然后只需将XAML中的属性绑定到CheckBoxIsChecked即可。

<CheckBox HorizontalAlignment="Center" IsChecked="{Binding IsChecked}"/> 

使用命令

您可以使用命令来封装逻辑并将其公开为可绑定对象。为此,您需要一个 中继命令 或通常称为 委托命令 的实现。您可以在以下文章中阅读有关这些概念的详细信息。

中继命令只是简单地采取一个动作,它封装为一个对象,并且可以选择一个 can execute 判断动作是否可以执行的谓词。该对象可以从视图模型中公开并绑定在 XAML 中。中继命令实现 ICommand 接口,WPF 知道如何处理它。

您可以从任何地方复制中继命令实现,有很多实现,大多数 MVVM 框架已经提供了自己的实现。如果您选择了一个,请在 SimulatorControlViewModel 中公开一个命令 属性 并在构造函数中使用要调用的方法对其进行初始化(这可能会因您使用的具体命令实现而异)。

public class SimulatorControlViewModel : INotifyPropertyChanged
{
   public SimulatorControlViewModel()
   {
      Clicked = new RelayCommand(ExecuteClicked);
   }

   public ICommand Clicked { get; }

   private void ExecuteClicked(object isChecked)
   {
      if ((bool)isChecked)
         // ...do something.
      else
         // ...do something.
   }

   // ...other code.

   public event PropertyChangedEventHandler PropertyChanged;

   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }
}

然后将Clicked命令绑定到CheckBoxCommand属性,将CommandParameter绑定到自己的IsChecked属性(这是传递给 ExecuteChecked 方法的参数。

<CheckBox HorizontalAlignment="Center" Command="{Binding Clicked, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"/> 

使用行为调用方法

您可以安装 Microsoft.Xaml.Behaviors.Wpf NuGet 包,其中包含 CallMethodAction 允许在引发事件时在对象上调用方法。

<CheckBox HorizontalAlignment="Center">
   <b:Interaction.Triggers>
      <b:EventTrigger EventName="Click">
         <b:CallMethodAction MethodName="Checked_Triggered" TargetObject="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"/>
      </b:EventTrigger>
   </b:Interaction.Triggers>
</CheckBox> 

注意:方法签名必须与 public void Checked_Triggered() 完全匹配(public、return void 并且没有参数),否则您将收到一个异常,说明该方法未找到。