Reputation: 183
Why buttons in listbox can't run binded command? I bind this command for simple button(not template) and it's working perfectly. Main.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="lbTemp">
<StackPanel>
<TextBlock Height="50" Text="{Binding}"/>
<Button Height="20" Content="click" Command="{Binding Path=TestCommand}" CommandParameter="hello"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox x:Name="listBox" Width="500" Height="300" ItemTemplate="{StaticResource lbTemp}" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.MyData}"/>
<Button Command="{Binding Path=TestCommand}" CommandParameter="hello" Width="200" Height="40"/>
</Grid>
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public ViewModel() { }
public ObservableCollection<string> MyData {
get
{
return _MyData;
}
set
{
if (!_MyData.SequenceEqual(value))
{
_MyData = value;
}
OnPropertyChanged();
}
}
private ObservableCollection<string> _MyData = new ObservableCollection<string>();
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string caller="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
private ICommand _testCommand;
public ICommand TestCommand
{
get
{
return _testCommand;
}
set
{
if (_testCommand != value)
{
_testCommand = value;
OnPropertyChanged();
}
}
}
}
And Command.cs
public class RelayCommand : ICommand
{
public RelayCommand(Action action, Func<object, bool> canExecutePredicate)
{
if (action == null || canExecutePredicate == null)
throw new ArgumentNullException("Can't be null");
this._action = action;
this._canExecutePredicate = canExecutePredicate;
}
public RelayCommand(Action action) : this(action, (obj) => true) { }
Action _action;
Func<object, bool> _canExecutePredicate;
public event EventHandler CanExecuteChanged;
protected virtual void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
return _canExecutePredicate(parameter);
}
public void Execute(object parameter)
{
_action?.Invoke();
}
}
Can you say valid xaml example for this solution?
Upvotes: 0
Views: 53
Reputation:
I think it's because the TestCommand is a property on ViewModel - not on the elements (strings) of the MyData collection.
You'll have to make a class:
class MyItemClass : INotifyPropertyChanged
{
public string Text {get;set;}
private ICommand _testCommand;
public ICommand TestCommand
{
get
{
return _testCommand;
}
set
{
if (_testCommand != value)
{
_testCommand = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string caller="")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
}
You should then fill your MyData collection with items of that type where the TextBlocks binding should be changed to Text in the DataTemplate.
BTW: you don't have to have a relativesource reference in the itemssource binding of the list box. It inherits the DataContext from the Window, so it should be sufficient just to use {Binding MyData}.
Upvotes: 1
Reputation: 326
The DataContext
of the two Button
s are different. Let's take a look at the DataContext
for some of the elements in your view.
Window
's DataContext
is your ViewModel
class.ListBox
's DataContext
is the same as the Window
's. There should be no need for the use of RelativeSource
in the ItemsSource
binding you have set up.Button
outside the DataTemplate
also has the same DataContext
as the Window
. That's why this Command
binding works fine.Button
inside the DataTemplate
has a DataContext
of the particular item it is representing from the MyData
collection you have created inside your ViewModel
class. Importantly, it is not the ViewModel
class itself.Here is where I would use RelativeSource
.
<Button Height="20" Content="click" Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" CommandParameter="hello"/>
Please let me know if this does not work for you.
Upvotes: 2