Xamarin.forms Can';无法从代码中创建的数据模板中获取父绑定上下文

Xamarin.forms Can';无法从代码中创建的数据模板中获取父绑定上下文,xamarin.forms,data-binding,Xamarin.forms,Data Binding,我正在视图模型中用代码创建一个数据模板,并将其绑定到ListView,如下所示 <ListView x:Name="MainList ItemTemplate="{Binding JobListTemplate}" 我似乎无法将命令的父(列表视图)绑定上下文正确绑定到vm 我读过一些帖子,建议如果模板与页面不在同一个文件中,可能会出现问题 有什么想法吗?您对源代码引用的设置是错误的。请参阅此链接 var按钮=新建按钮 { Text=“单击我”, }; SetBindin

我正在视图模型中用代码创建一个数据模板,并将其绑定到ListView,如下所示

<ListView 
    x:Name="MainList
    ItemTemplate="{Binding JobListTemplate}"
我似乎无法将命令的父(列表视图)绑定上下文正确绑定到vm

我读过一些帖子,建议如果模板与页面不在同一个文件中,可能会出现问题


有什么想法吗?

您对源代码引用的设置是错误的。请参阅此链接

var按钮=新建按钮
{
Text=“单击我”,
};
SetBinding(button.CommandProperty,新绑定(“BindingContext.ClickMeCommand”,来源:listView));

这是一个相当复杂的问题,涉及到各种SO答案和Xamarin论坛帖子,所以我想我应该发布答案

首先,我创建了一个ViewCell类,在其中我用代码组装了ViewCell。注意_favoriteButton变量

public class JobListViewCell : ViewCell
{
    private Button _favouriteButton;

    public JobListViewCell()
    {
        BuildViewCell();
    }
我创建了按钮并绑定了命令参数属性-这将传递列表视图项

_favouriteButton = new Button();
_favouriteButton.SetDynamicResource(VisualElement.StyleProperty, "IconButtonSolid");
_favouriteButton.Text = FontAwesomeIcons.Heart;
_favouriteButton.SetBinding(Button.CommandParameterProperty, new Binding("."));
我创建了一个名为ParentBindingContext的可绑定属性-这将允许我在buttons命令上更新绑定上下文

//
// ParentBindingContext
//
public static readonly BindableProperty ParentBindingContextProperty =
    BindableProperty.Create(nameof(ParentBindingContext), typeof(object), 
        typeof(JobListViewCell), "", BindingMode.OneWay, null,
        OnParentBindingContextPropertyChanged);

private static void OnParentBindingContextPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
    if (bindable is JobListViewCell control)
    {
        control._favouriteButton?.SetBinding(Button.CommandProperty,
            new Binding("ToggleIsFavouriteCommand",
                BindingMode.Default, null, null, null,
                newvalue));
    }
}

public object ParentBindingContext
{
    get => (object)GetValue(ParentBindingContextProperty);
    set => SetValue(ParentBindingContextProperty, value);
}
最后,在我的页面XAML中,我将视图单元格和绑定的ParentBindingContext添加到列表视图中

<ListView
    Grid.Row="1"
    ItemsSource="{Binding Jobs}"
    SelectedItem="{Binding SelectedJob, Mode=TwoWay}"
    x:Name="MainList">
    <ListView.ItemTemplate>
        <DataTemplate>
            <viewCells:JobListViewCell 
                ParentBindingContext="{Binding BindingContext, Source={x:Reference MainList}}">
            </viewCells:JobListViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

以下是视图模型中的命令。将传入包含已按下按钮的列表视图项

private Command _toggleIsFavouriteCommand;
public Command ToggleIsFavouriteCommand => _toggleIsFavouriteCommand ??= new Command<JobMaster>(ToggleIsFavourite);

private void ToggleIsFavourite(JobMaster jobMaster)
{
    jobMaster.IsFavourite = !jobMaster.IsFavourite;
}
private命令\u toggleisfavorite命令;
公共命令toggleisfavoriteCommand=>\u toggleisfavoriteCommand???=新命令(toggleisfavorite);
专用无效切换IsFavorite(作业主机作业主机)
{
jobMaster.isfavorite=!jobMaster.isfavorite;
}

希望这对其他人有所帮助。

数据模板的父级ListView不在代码文件中,因此我认为我无法以这种方式引用它。我尝试了source:“MainList”,但没有成功。@SteveChadbourne—“MainList”应该可以成功-x:Name使变量名在代码隐藏中可见。但是Intellisense在您第一次成功构建之前不会知道这一点。在此之前,您将看到一个生成错误列表。解决方法是将x:Name添加到XAML,构建项目,然后开始向code-behind.FWIW添加所需的内容,在您展示的简单场景中,IMHO可以简单地将
null
作为源代码传入。默认情况下,页面上的所有元素都使用页面的
BindingContext
。它应该“刚刚起作用”。不需要对
MainList
的引用,除非它或其祖先之一重写了页面的绑定上下文。(虽然我不能100%确定您使用的是
Cell
TemplateBinding
,但从模板上下文到页面的视图层次结构可能需要更多内容。)大约有5-6篇文章讨论了从代码而不是xaml引用父BindingContex的方法。不管什么奇怪的原因。您还应该将此标记为答案。
<ListView
    Grid.Row="1"
    ItemsSource="{Binding Jobs}"
    SelectedItem="{Binding SelectedJob, Mode=TwoWay}"
    x:Name="MainList">
    <ListView.ItemTemplate>
        <DataTemplate>
            <viewCells:JobListViewCell 
                ParentBindingContext="{Binding BindingContext, Source={x:Reference MainList}}">
            </viewCells:JobListViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
private Command _toggleIsFavouriteCommand;
public Command ToggleIsFavouriteCommand => _toggleIsFavouriteCommand ??= new Command<JobMaster>(ToggleIsFavourite);

private void ToggleIsFavourite(JobMaster jobMaster)
{
    jobMaster.IsFavourite = !jobMaster.IsFavourite;
}