Reputation: 6953
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
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
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