1

I seem to be having issues with getting a command wired up to a button.

Suppose I have the following:

MainPageViewModel.cs

public class MainPageViewModel : ViewModelBase
{
    private ICollection<MenuItem> _menuItems;

    public MainPageViewModel(INavigationService navigationService) : base(navigationService)
    {
        TestCommand = new DelegateCommand(TestCommandExecute);
        Title = "Menu";
    }

    public ICollection<MenuItem> MenuItems
    {
        get
        {
            return _menuItems;
        }

        private set
        {
            SetProperty(ref _menuItems, value, nameof(MenuItems));

            return;
        }
    }

    public DelegateCommand TestCommand
    {
        get;

        private set;
    }

    private void TestCommandExecute()
    {
        return;
    }
}

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Menu.Views.MainPage" x:Name="Root" Title="{Binding Title}">
    <StackLayout>
        <Button Command="{Binding Mode=OneWay, Path=TestCommand}" Text="Test" />
        <ListView ItemsSource="{Binding Mode=OneWay, Path=MenuItems}" SeparatorVisibility="None">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <ViewCell.View>
                            <Button Command="{Binding Path=TestCommand, Source={x:Reference Name=Root}}" Text="{Binding Path=Title}" />
                        </ViewCell.View>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

The command will fire when I click the Test button but it will not fire when I click any of the buttons that are generated by way of the ListView. I have confirmed this by placing a break point on the TestCommandExecute method. I do not see any errors being generated.

Is this the proper way to wire up buttons inside of a list view to a command in the view model?

Jason Richmeier
  • 1,595
  • 3
  • 19
  • 38
  • Those things in the list should be view models, too... with their own commands, potentially... – Haukinger Jul 29 '19 at 19:20
  • @Haukinger - Why do the things in the list need to be view models themselves? At any rate, I am not trying to bind to a command on the list item. Instead, I am trying to bind to a command on the page view model. Each button will fire the same command and the appropriate action will be driven by the command parameter. – Jason Richmeier Jul 29 '19 at 19:25
  • @Haukinger that is terrible advice, and 100% WRONG! You should not be creating ViewModels just to bind to a ListView. – Dan Siegel Jul 30 '19 at 02:40
  • @DanS. then XF is indeed much more different from WPF than I thought - I always had positive experiences from adhering to "do not bind to the model directly". – Haukinger Jul 30 '19 at 07:32
  • @Haukinger it's not that XF is different than WPF, what you're suggesting by wrapping everything inside of it's own ViewModel misses the entire concept of what MVVM is. You should make your Model implement INotifyPropertyChanged but that doesn't make them a ViewModel, nor should you have ViewModels that try to have a list of ViewModels like what you're suggesting. That's just bad design. – Dan Siegel Jul 30 '19 at 14:44
  • @DanS. for me, having a `public SomeModelType SomeProperty { get; }` on a view model is bad design, most of the time. It may suffice in trivially simple cases, but it's certainly not what one wants to do. – Haukinger Jul 30 '19 at 19:16
  • @Haukinger you're overcomplicating your design reducing the maintainability of your apps and offering very bad advice here. – Dan Siegel Jul 31 '19 at 04:47
  • @DanS. in very simple cases, using the model directly may be fine, i.e. if the model is essentially a single string. But I have never seen that in real apps, it's always more complicated... – Haukinger Jul 31 '19 at 07:21
  • @Haukinger yes and I usually get brought in to clean up the mess that devs make following that sort of terrible advice from a lack of understanding how to break it down into something that is maintainable. – Dan Siegel Jul 31 '19 at 14:51
  • @DanS. same here, yet bad code does not come by itself - stupid devs make bad code, and most of devs are stupid, unfortunately. But that does not make having a clean design with clear responsibilities bad advice. – Haukinger Jul 31 '19 at 18:18

2 Answers2

1

I figured this out via another Stack Overflow question...

The binding should be...

<Button Command="{Binding Path=BindingContext.TestCommand, Source={x:Reference Name=Root}}" Text="{Binding Path=Title}" />

...instead of...

<Button Command="{Binding Path=TestCommand, Source={x:Reference Name=Root}}" Text="{Binding Path=Title}" />

Notice the added BindingContext.

Jason Richmeier
  • 1,595
  • 3
  • 19
  • 38
  • While technically correct it is worth pointing out this is overly verbose. You do not need to specify the Path attribute of a Binding nor do you need to specify the Name attribute for the Reference. it should look more like `{Binding BindingContext.TestCommand, Source={x:Reference Root}}` – Dan Siegel Jul 29 '19 at 20:01
1

Try using this:

<Button Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainPage}}}"/>
user947737
  • 326
  • 2
  • 11
  • It should be `{Binding DataContext.TestCommand, Source={RelativeSource FindAncestor, AncestorType={x:Type local:MainPage}}}`, but otherwise nice solution! – RickardN Jun 22 '20 at 09:12