Bind button in DataTemplate to command in the form's ViewModel

27,041

Solution 1

It's:

{Binding DataContext.FireCommand,
         RelativeSource={RelativeSource AncestorType=ListBox}}

No need to walk up to the root unless you actually change the DataContext along the way, but as the ListBox seems to bind to a property on the main VM this should be enough.

The only thing i recommend reading is the Data Binding Overview, and the Binding class documentation (including its properties).


Also here is a short explanation on how bindings are constructed: A binding consists of a source and a Path relative to that source, by default the source is the current DataContext. Sources that can be set explicitly are: Source, ElementName & RelativeSource. Setting any of those will override the DataContext as source.

So if you use a source like RelativeSource and want to access something in the DataContext on that level the DataContext needs to appear in the Path.

Solution 2

This may be considered unrelated by most, but this search is only 1 of 3 results that you'll find searching for data binding commands to controls inside a data template--as it relates to Xamarin Forms. So, maybe it'll help someone now-a-days.

Like me you may wonder how to bind commands inside a BindableLayout. Credit jesulink2514 for answering this at Xamarin Forums, where it's probably overlooked by many because of all the comments. Here's his solution, but I'm including the link below:

<ContenPage x:Name="MainPage">
<ListView Grid.Row="1"
              ItemsSource="{Binding Customers}"
              VerticalOptions="Fill"
              x:Name="ListviewCustomer">
      <ListView.ItemTemplate>
        <DataTemplate>
      <Label Text="{Binding Property}"/>
          <Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}" 
                         CommandParameter="{Binding .}">Click me</Button>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
</ContentPage>

https://forums.xamarin.com/discussion/comment/217355/#Comment_217355

Share:
27,041
nedhenry
Author by

nedhenry

Some of my hobby projects: SCM Backup - Makes offline backups of your cloud hosted source code repositories MissileSharp - .NET library to control an USB Missile Launcher SimpleLeague - minimalistic tool to display tabular league data in existing websites. RoboShell Backup - simple personal backup tool (PC → NAS → USB disk) Contact me: mail (at) my website

Updated on September 19, 2020

Comments

  • nedhenry
    nedhenry over 3 years

    My problem is similar to the one described in this question:
    WPF MVVM Button Control Binding in DataTemplate

    Here is my XAML:

    <Window x:Class="MissileSharp.Launcher.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MissileSharp Launcher" Height="350" Width="525">
        <Grid>
            <!-- when I put the button here (outside the list), the binding works -->
            <!--<Button Content="test" Command="{Binding Path=FireCommand}" />-->
            <ListBox ItemsSource="{Binding CommandSets}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <!-- I need the button here (inside the list), and here the binding does NOT work -->
                        <Button Content="{Binding}" Command="{Binding Path=FireCommand}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    

    It's just a ListBox, bound to an ObservableCollection<string> named CommandSets (which is in the ViewModel).
    This binding works (it displays a button for each item in the collection).

    Now I want to bind the button to a command (FireCommand), which is also in the ViewModel.
    Here's the relevant part of the ViewModel:

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public ICommand FireCommand { get; set; }
        public ObservableCollection<string> CommandSets { get; set; }
    
        public MainWindowViewModel()
        {
            this.FireCommand = new RelayCommand(new Action<object>(this.FireMissile));
        }
    
        private void FireMissile(Object obj)
        {
            System.Windows.MessageBox.Show("fire");
        }
    }
    

    The binding of this button does NOT work.
    From what I've understood from the question I linked above, the binding doesn't work because:
    (correct me if I'm wrong)

    • The button is inside the ListBox, so it only "knows" the binding of the ListBox (the ObservableCollection, in this case), but not the binding of the main window
    • I'm trying to bind to a command in the main ViewModel of the main window (which the button doesn't "know")

    The command itself is definitely correct, because when I put the button outside the ListBox (see the XAML above for an example), the binding works and the command is executed.

    Apparently, I "just" need to tell the button to bind to the main ViewModel of the form.
    But I'm not able to figure out the right XAML syntax.

    I tried several approaches that I found after some googling, but none of them worked for me:

    <Button Content="{Binding}" Command="{Binding RelativeSource={RelativeSource Window}, Path=DataContext.FireCommand}" />
    
    <Button Content="{Binding}" Command="{Binding Path=FireCommand, Source={StaticResource MainWindow}}" />
    
    <Button Content="{Binding}" Command="{Binding Path=FireCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
    

    Could someone please:

    1. give me the proper XAML to bind the button inside the ListBox to a command in the form's MainViewModel?
    2. point me to a link where this advanced binding stuff is explained in a way that a WPF/MVVM beginner can understand?
      I'm feeling like I'm just copying and pasting arcane XAML incantations, and so far I don't have any clue (and can't find any good documentation) how I would figure out by myself in which cases I'd need RelativeSource or StaticResource or whatever instead of a "normal" binding.