从 ListItem 内部触发外部 ICommands/Functions

Triggering External ICommands/Functions from inside a ListItem

我想知道从内部列表项中触发外部函数(或命令)的最佳方法是什么。

例如,我有一个 FooManager 对象,它包含一个名为 MyFoosFoo 个对象的 ObservableCollection。 FooManager 还有一个名为 ProcessFoo(Foo foo).

的函数
<StackPanel DataContext="FooManager">
   <ListView ItemsSource="{Binding MyFoos}" >
        <ListView.ItemTemplate>
            <DataTemplate>
                <WrapPanel>
                    <Button Content="Do Something"
                            Command="{Binding Path=SomeFooCommand} />
                </WrapPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>

如何通过单击 "Do Something" 按钮触发 ProcessFoo(Foo foo) 函数(并传递 'clicked' Foo)?

我知道我可以使用代码隐藏来执行此操作,但我想知道最干净的 MVVM 方法是什么。 Foo 的 ViewModel 是否应该包含对其 FooManager 的引用,或者 ViewModel 相互引用是不好的做法吗?

我使用 Prism 的 DelegateCommand,我认为这是绑定到命令的最简单方法。它与许多其他命令选项非常通用。如果您有 visual studio,请转到工具 > NuGet 包管理器 > 管理包,在浏览中搜索 Prism 并安装 Prism.Core。或者您可以创建自己的 DelegateCommand 实现 ICommand 接口。 delegatecommand 的另一个选项在本文底部:https://www.codeproject.com/Articles/36545/WPF-MVVM-Model-View-View-Model-Simplified,它使用 wpf mvvm 工具包。这是棱镜的实现:

view.xaml:

<Button Command="{Binding FooCommand}" />

viewmodel.cs:

  public DelegateCommand FooCommand { get; private set; }
  FooCommand = new DelegateCommand(OnFoo, CanFoo);
  private void OnFoo()
  {
     ProcessFoo(ListViewName.SelectedItems);
  }
  private bool CanFoo()
  {
     return someBooleanFunc();
  }

您可能希望将 "FooCommand = new ..." 移动到 ViewModel 构造函数。

感谢@Clemens。在 FooManager class 中,添加一个以 Foo VM 作为参数的命令。然后,DataTemplate 中的 Button 可以通过在绑定中使用 RelativeSource 并将自身(空 {Binding})作为参数传递来触发它。

<StackPanel DataContext="FooManager">
   <ListView ItemsSource="{Binding MyFoos}" >
        <ListView.ItemTemplate>
            <DataTemplate>
                <WrapPanel>
                    <Button Content="Do Something"
                            Command="{Binding Path=DataContext.ProcessFoo, RelativeSource={RelativeSource AncenstorType=ListView}}
                            CommandParameter={Binding} />
                </WrapPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>