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);
    }
}
4
  • 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 ? Commented Feb 6, 2019 at 8:02
  • And also the RelayCommand.Execute method has an object parameter, why isn't that applied to your AddNewTimesheetEntry ? Commented Feb 6, 2019 at 8:03
  • I made a breakpoint inside desired method and inside RelayCommand.Execute and saw that they are not called. Commented Feb 6, 2019 at 8: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. Commented Feb 6, 2019 at 9:30

1 Answer 1

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}}}"
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.