将 ItemsControl 中的 Checkbox Click 事件绑定到 viewmodel
Binding Checkbox Click event inside ItemsControl to viewmodel
我有一个 ItemsControl
可以根据 DigitalFilterSubsystems
创建 CheckBox
es。我想在单击 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中的属性绑定到CheckBox
的IsChecked
即可。
<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
命令绑定到CheckBox
的Command
属性,将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
并且没有参数),否则您将收到一个异常,说明该方法未找到。
我有一个 ItemsControl
可以根据 DigitalFilterSubsystems
创建 CheckBox
es。我想在单击 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中的属性绑定到CheckBox
的IsChecked
即可。
<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
命令绑定到CheckBox
的Command
属性,将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
并且没有参数),否则您将收到一个异常,说明该方法未找到。