Bind button in DataTemplate to command in the form's ViewModel
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
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, 2020Comments
-
nedhenry over 3 years
My problem is similar to the one described in this question:
WPF MVVM Button Control Binding in DataTemplateHere 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 anObservableCollection<string>
namedCommandSets
(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 theListBox
(theObservableCollection
, 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:
- give me the proper XAML to bind the button inside the
ListBox
to a command in the form'sMainViewModel
? - 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 needRelativeSource
orStaticResource
or whatever instead of a "normal" binding.
- The button is inside the