0

I need to have a button in each ListViewItem. I've created the Button in DataTemplate, bound the command and it doesn't get executed when I press the button. It just doesn't being called.

I was referring to different tutorials and questions like WPF Button doesn't execute Command or How to bind WPF button to a command in ViewModelBase? and created a RelayCommand class, which implements ICommand.

Actually, I need to call the action with the parameter, but I can't even get it to work without parameters, so I'm planning to get to it next. Everything else is bound perfectly and works like a charm.

View

<Page.Resources>
<CollectionViewSource x:Key='src' 
              Source="{Binding TimesheetEntries}"
                      >
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Date" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Page.Resources>
<Page.DataContext>
    <ViewModels:TimesheetViewModel/>
</Page.DataContext>

<ListView 
    x:Name="TimesheetEntriesListView"
    Margin="10"
    Grid.Row="1"
    Grid.ColumnSpan="2"
    ItemsSource="{Binding Source={StaticResource src}}"
    SelectedItem="{Binding SelectedEntry, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    >
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Height="30" Margin="3" IsEnabled="{Binding IsEditable}">
                <ComboBox 
                    SelectedValuePath="Key" DisplayMemberPath="Value" 
                    ItemsSource="{Binding EmploymentTypesDictionary, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                    SelectedValue="{Binding SelectedEmployment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                    Width="300"/>
                <TextBox 
                    Text="{Binding Hours, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat=N2}" 
                    Margin="5,0,0,0"
                    Height="Auto"
                    IsEnabled="{Binding HoursAvaliable}"
                    Width="70"/>
                <Button Margin="5,0,10,0" 
                        Content="+"
                        Command="{Binding AddNewTimesheetEntryCommand}"
                        CommandParameter="{Binding Path=Name}"
                ></Button>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
    <ListView.GroupStyle>
        <GroupStyle>
            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <StackPanel Margin="5,5,5,0" Orientation="Horizontal">
                        <TextBlock  FontSize="14" Text="{Binding Path=Name, StringFormat='{}{0:dd/MM/yyyy, dddd}'}"/>
                    </StackPanel>
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </ListView.GroupStyle>
</ListView>

ViewModel

class TimesheetViewModel : BaseViewModel
{
    public ICommand AddNewTimesheetEntryCommand
    {
        get
        {
            return _AddNewTimesheetEntryCommand ?? new RelayCommand(AddNewTimesheetEntry);
        }
    }

    private ICommand _AddNewTimesheetEntryCommand;

    public void AddNewTimesheetEntry(object parameter)
    {
        //Do stuff
    }

    public TimesheetViewModel()
    {

    }
}

RelayCommand

public class RelayCommand : ICommand
{

    private Action<object> mAction;

    public event EventHandler CanExecuteChanged = (sender, e) => { };

    public RelayCommand(Action<object> action)
    {
        mAction = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        mAction(parameter);
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Tumist
  • 119
  • 3
  • 11
  • 1
    First of all, why does your command have a setter ? Do you ever change the command ? You could change it to `public ICommand AddNewTimesheetEntryCommand {get;}` and only set it once in the constructor like `AddNewTimesheetEntryCommand = new RelayCommand(AddNewTimesheetEntry)`. When that is said. How did you determine it was not working ? – Nawed Nabi Zada Feb 06 '19 at 08:02
  • And also the RelayCommand.Execute method has an object parameter, why isn't that applied to your AddNewTimesheetEntry ? – Nawed Nabi Zada Feb 06 '19 at 08:03
  • I made a breakpoint inside desired method and inside RelayCommand.Execute and saw that they are not called. – Tumist Feb 06 '19 at 08:19
  • I didn't apply parameter to my method because, well, i thought that i might've made a mistake making an Action with parameter, so I decided to try the "clean" version for testing purposes. – Tumist Feb 06 '19 at 09:30

1 Answers1

1

Your button need to have been different bind, beacuse inside the list-template you do not have access to global DataContext only to local. You need to use relative source to access global DataContext.

Command="{Binding Path=DataContext.AddNewTimesheetEntryCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"
Kaspar
  • 405
  • 4
  • 13