在DataTemplate中将按钮与表单ViewModel中的命令绑定

34

我的问题与这个问题描述的类似:
WPF MVVM中DataTemplate中按钮控件绑定问题

这是我的XAML代码:

<Window x:Class="MissileSharp.Launcher.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MissileSharp Launcher" Height="350" Width="525">
    <Grid>
        <!-- when I put the button here (outside the list), the binding works -->
        <!--<Button Content="test" Command="{Binding Path=FireCommand}" />-->
        <ListBox ItemsSource="{Binding CommandSets}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <!-- I need the button here (inside the list), and here the binding does NOT work -->
                    <Button Content="{Binding}" Command="{Binding Path=FireCommand}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

这只是一个绑定到 ViewModel 中名为 CommandSets 的 ObservableCollection<string> 的 ListBox (它会显示集合中每个项目的按钮)。

现在我想将按钮绑定到 ViewModel 中的一个命令(FireCommand)。以下是 ViewModel 中相关部分的代码:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public ICommand FireCommand { get; set; }
    public ObservableCollection<string> CommandSets { get; set; }

    public MainWindowViewModel()
    {
        this.FireCommand = new RelayCommand(new Action<object>(this.FireMissile));
    }

    private void FireMissile(Object obj)
    {
        System.Windows.MessageBox.Show("fire");
    }
}
这个按钮的绑定无法正常工作
从我在上面链接的问题中所理解的来看,绑定不起作用是因为:
(如果我错了,请纠正我)

  • 按钮位于ListBox内部,因此只“知道”ListBox的绑定(在此情况下为ObservableCollection),而不知道主窗口的绑定
  • 我试图将其绑定到主窗口的主ViewModel中的命令(该按钮不“知道”这个命令)

命令本身肯定是正确的,因为当我将按钮放在ListBox之外(请参见上面的XAML示例)时,绑定会生效并且命令会执行。

显然,“只需要”告诉按钮绑定到表单的主ViewModel。
但我无法弄清楚正确的XAML语法。

我尝试了一些在Google上找到的方法,但它们都对我不起作用:

<Button Content="{Binding}" Command="{Binding RelativeSource={RelativeSource Window}, Path=DataContext.FireCommand}" />

<Button Content="{Binding}" Command="{Binding Path=FireCommand, Source={StaticResource MainWindow}}" />

<Button Content="{Binding}" Command="{Binding Path=FireCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

请问能否:

  1. 提供适用于ListBox中的按钮与表单MainViewModel中命令绑定的正确XAML代码?
  2. 能否指导一个WPF/MVVM初学者理解高级绑定操作的相关信息?
    我感觉我只是在复制和粘贴晦涩难懂的XAML代码,而且迄今为止我不知道(也找不到好的文档)在哪些情况下我需要使用RelativeSourceStaticResource甚至其他替代"正常"绑定的方式。
2个回答

78

它是:

{Binding DataContext.FireCommand,
         RelativeSource={RelativeSource AncestorType=ListBox}}
不需要一直遍历至根级别,除非您实际上在路径中更改了DataContext。但由于ListBox似乎绑定到主VM的属性上,这应该已经足够了。
我唯一建议阅读的是数据绑定概述,以及Binding文档(包括它的属性)。
此外,以下是有关如何构建绑定的简要说明:绑定由一个和相对于该Path组成,默认情况下是当前的DataContext。可以显式设置的源有:SourceElementNameRelativeSource。设置其中任何一个都将覆盖DataContext作为的默认设置。
因此,如果您使用像RelativeSource这样的并希望访问该级别上的DataContext中的内容,则需要在Path中出现DataContext

非常好的回答,这很有帮助。谢谢! - WinW
谢谢!终于解决了四个小时的调试问题!顺便说一下,如果你在UserControl中设置了x:Name="root",它也可以与{Binding DataContext.FireCommand, ElementName=root}一起使用。我认为这样稍微更加优雅一些。 - Informagic
@Informagic:除非你的IDE可以在重命名元素时更新绑定,否则我建议使用相对源绑定。如果您滥用名称引用并绑定到“远处”的元素,则还会使理解标记变得困难。 - H.B.
在我的场景中,使用{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.FireCommand}进行工作,希望这能帮助到某些人。 - Peter-Yu
@Peter-Yu:在大多数情况下,这是等效的。您只需直接设置“Path”属性即可。在我的答案中,路径被传递给“Binding”构造函数,该构造函数在内部设置了“Path”属性。(但是,在使用构造函数时可能会出现一些实现细节问题。) - H.B.

5
这可能被大多数人视为无关,但这个搜索结果是在搜索与Xamarin Forms相关的数据模板中控件绑定命令时找到的3个结果之一。因此,也许现在它会帮助某个人。

像我一样,您可能想知道如何在BindableLayout中绑定命令。感谢jesulink2514在Xamarin论坛上回答这个问题,因为由于众多评论,它可能被很多人忽略了。这是他的解决方案,但我包含了下面的链接:

<ContenPage x:Name="MainPage">
<ListView Grid.Row="1"
              ItemsSource="{Binding Customers}"
              VerticalOptions="Fill"
              x:Name="ListviewCustomer">
      <ListView.ItemTemplate>
        <DataTemplate>
      <Label Text="{Binding Property}"/>
          <Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}" 
                         CommandParameter="{Binding .}">Click me</Button>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
</ContentPage>

https://forums.xamarin.com/discussion/comment/217355/#Comment_217355


作为额外的资源,此链接中的代码在我的情况下有效:https://www.syncfusion.com/kb/11029/how-to-bind-command-from-viewmodel-to-external-itemtemplate-of-xamarin-forms-listview - Daniel Valcarce
绝对帮助到了我。 - user2404597

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接