Steve Chadbourne
Steve Chadbourne

Reputation: 6953

Can't get parent binding context from data template created in code

I'm creating a data template in code in a view model and binding it to a ListView like so

<ListView 
    x:Name="MainList
    ItemTemplate="{Binding JobListTemplate}"

I have a button in the template that binds to a command in the view model.

Previously the data template was hard coded in the page XAML and the button looked like this

<Button
    Text="Click me"
    Command="{Binding Path=BindingContext.ClickMeCommand, Source={x:Reference MainList}}"
    CommandParameter="{Binding .}" />

All worked well.

Now the button is created in code like this

var button = new Button();
button.Text = "Click me;
button.SetBinding(Button.CommandProperty, new Binding("BindingContext.ClickMeCommand", 
    BindingMode.Default, null, null, null, 
    new ReferenceExtension { Name = "MainList" }));

and I can't seem to get the parent (list view) binding context for the command properly bind to the vm.

I have read some posts that suggest it can be an issue if the template is not in the same file as the page.

Any ideas?

Upvotes: 1

Views: 1465

Answers (2)

Steve Chadbourne
Steve Chadbourne

Reputation: 6953

This was quite complex and took parts from various SO answers and Xamarin forum posts so I thought I'd post the answer.

First I created a ViewCell class where I assembled the view cell in code. Note the _favouriteButton variable.

public class JobListViewCell : ViewCell
{
    private Button _favouriteButton;

    public JobListViewCell()
    {
        BuildViewCell();
    }

I created the button and bound the command parameter property - this will pass the list view item

_favouriteButton = new Button();
_favouriteButton.SetDynamicResource(VisualElement.StyleProperty, "IconButtonSolid");
_favouriteButton.Text = FontAwesomeIcons.Heart;
_favouriteButton.SetBinding(Button.CommandParameterProperty, new Binding("."));

I created a bindable property called ParentBindingContext - this will allow me to update the binding context on the buttons command

//
// 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);
}

Finally, in my page XAML I added the view cell and bound ParentBindingContext to the list view.

<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>

Here is the command in the view model. The list view item containing the button that was pressed is passed in.

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

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

Hope this helps someone.

Upvotes: 1

Ganesan VG
Ganesan VG

Reputation: 176

Your setting the source reference is wrong. Refer to this link Data Binding.

var button = new Button
{
    Text = "Click me",
};
button.SetBinding(Button.CommandProperty, new Binding("BindingContext.ClickMeCommand",source: listView));

Upvotes: 0

Related Questions