如何从 DataTemplate 中正确绑定到 VM 属性

How to properly bind to VM Properties from within a DataTemplate

问题:

我有一个带有 ListView 的页面来显示 object 的列表。我根据 Microsoft 的 Creating a DataTemplate with a TypeDataTemplate 分离到它自己的文件中。我希望能够编辑或删除此列表中的条目,因此我一直在使用 ViewCellContextActions MenuItem 来提供此功能。这一直有效,直到我不得不将我的 DataTemplate 与 ContentPage 分开。处理编辑和删除的命令位于我的 ContentPageBindingContext,即 ViewModel.

我试过的东西

多种绑定格式,FindAncestorBindingContextFindAncestor两种RelativeSource模式,多种AncestorType

{Binding BindingContext.EditCommand, Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContentPage}}}
{Binding Source={RelativeSource Mode=FindAncestorBindingContext, AncestorType={x:Type views:LoginView}}, Path=EditCommand}
{Binding Source={RelativeSource AncestorType={x:Type vm:LoginVM}}, Path=BindingContext.EditCommand}

问题:

我如何从分离的 DataTemplate 绑定到我的 ViewModel 中的命令? 我尝试使用 RelativeSource 绑定但是该命令永远不会被触发,或者永远找不到祖先。这是我的基本设置:

内容页面(登录视图):

....
<ContentPage.BindingContext>
   <vm:LoginVM x:Name="viewmodel"/>
</ContentPage.BindingContext>
    
<ContentPage.Content>
     ....
     <Frame HasShadow="True" Padding="30" VerticalOptions="End" BackgroundColor="{DynamicResource PageBackgroundColor}">
          <ListView ItemsSource="{Binding sessionList}" SelectedItem="{Binding SelectedItem}" HasUnevenRows="True" >
               <ListView.ItemTemplate>
                       <DataTemplate>
                            <templates:LoginSessionTemplate/>
                       </DataTemplate>
                  </ListView.ItemTemplate>
             </ListView>
        </Frame>
</ContentPage.Content>

数据模板:

<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
          xmlns:views="clr-namespace:PIM.View"
          x:Class="PIM.DataTemplates.LoginSessionTemplate">
<ViewCell.ContextActions>
    <MenuItem Command="{Binding Source={RelativeSource AncestorType={x:Type views:LoginView}}, Path=EditCommand}" CommandParameter="{Binding .}" Text="Edit"/>
</ViewCell.ContextActions>
....

模板和数据 object 之间的绑定很好,我只是在从分离的 DataTemplate 中绑定到 ViewModel 时遇到了麻烦。使用上面的代码不会触发命令。出现上下文菜单并且按钮的文本是正确的,但是该命令从未执行或从未找到 ViewModelBindingContext。到目前为止,我唯一的解决方案是将 DataTemplate 包含在 ContentPage 中,这实际上违背了首先将 DataTemplate 分开的目的。感谢任何帮助,谢谢!

更新与回答

ToolmakerSteve 非常友好地 link 了一个话题,虽然 linked 的答案没有用,但被接受的话题答案确实有效。由于某种原因 FindAncestor 在我的情况下不起作用,我必须通过 XAML 层次结构引用 ViewModel。我不确定这对 MVVM 来说是否是好的做法,但它确实有效。

TL;DR:

要从分离的 DataTemplate 中引用您的 ViewModel,您必须遍历 XAML 层次结构。为此,请为您的 DataTemplateViewCell 提供一个 x:Name 参数,然后在您的绑定中使用引用。该绑定将访问 ViewCell 的 parents。绑定应如下所示:

{Binding Parent.Parent.BindingContext.MyCommand, Source={x:Reference ViewCellName}}

我只需要改变 DataTemplate

<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
          xmlns:views="clr-namespace:PIM.View"
          x:Class="PIM.DataTemplates.LoginSessionTemplate"
          x:Name="VC">
<ViewCell.ContextActions>
    <MenuItem Command="{Binding Parent.Parent.BindingContext.EditCommand, Source={x:Reference VC}}" CommandParameter="{Binding .}" Text="Edit"/>
</ViewCell.ContextActions>
....

@ToolmakerSteve 在评论中提出的建议没有帮助,但同一个线程的实际答案确实有帮助。我能够通过引用 ViewCell 的父级而不是使用 FindAncestor.

来绑定到我的 ViewModel

您必须给 DataTemplateViewCell 一个 x:Name 参数才能引用它。接下来是找出在到达具有 BindingContext 的元素之前必须遍历多远的层次结构 您正在寻找的对象。就我而言,我第一次选择了两个级别,并且成功了。这是我修改后的绑定,其中 VC 是我的 ViewCellx:Name.

{Binding Parent.Parent.BindingContext.EditCommand, Source={x:Reference VC}}

我觉得很奇怪 FindAncestor 在这种情况下不起作用。也许这是一个错误?不管怎样,谢谢你的帮助。