1
votes

I have a button in the header of a datagrid column, but it is not binding to the property on the view model.

Here is the XAML:

    <DataGrid ItemsSource="{Binding BillingHistoryList}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding ar_bill_dt}">
                <DataGridTextColumn.Header>
                    <Button Content="Bill Date" Command="{Binding SortData}" />
                </DataGridTextColumn.Header>
            </DataGridTextColumn>
            <DataGridTextColumn Header="Amount" Binding="{Binding ar_orig_amt}" />
            <DataGridTextColumn Header="Running Total" Binding="{Binding RunningTotal}" />
        </DataGrid.Columns>
    </DataGrid>

The data is displayed in the grid, but when I press the button it does not execute. The error in the output is:

System.Windows.Data Error: 40 : BindingExpression path error: 'SortData' property not found on 'object' ''Button' (Name='')'. BindingExpression:Path=SortData; DataItem='Button' (Name=''); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')

If I move the button outside the datagrid, it works. I'm guessing that the problem is the path to the button property, but I don't know how to specify it so that it works.

What do I need to do to get the button to work?

Here's the pertinent parts of the view model:

public class BillingHistoryViewModel: ValidatingBindableBase, IRegionManagerAware, INavigationAware
{
    public IRegionManager RegionManager { get; set; }

    private ICustomerService _service;
    private ITransactionHistoryService _trnsService;
    private string _Title;
    public string Title { get => _Title; set => SetProperty(ref _Title, value); }

    public BillingHistoryViewModel(ICustomerService service, ITransactionHistoryService trnsService)
    {
        Title = "Billing History";
        _service = service;
        _trnsService = trnsService;
        SortData = new DelegateCommand(ExecuteSortData);
    }

    private void ExecuteSortData()
    {
    }


    void GetBillingHistory(int account)
    {
        var CustId = _trnsService.GetCustId(account);

        var BillHistList = new List<BillHist>();

        var BillHistoryEntries = _trnsService.GetEntriesByCustId(CustId);

        decimal runningTotal = 0.0m;

        foreach(var entry in BillHistoryEntries)
        {
            var newEntry = new BillHist(entry);

            newEntry.RunningTotal = runningTotal;
            runningTotal += newEntry.ar_orig_amt.Value;

            BillHistList.Add(newEntry);
        }

        BillHistList.Add(new BillHist() {ar_bill_dt=DateTime.Now, ar_orig_amt=10.0m, RunningTotal = 100.0m });
        BillingHistoryList = new ListCollectionView(BillHistList);
    }


    private ICollectionView _BillingHistoryList;
    public ICollectionView BillingHistoryList { get => _BillingHistoryList; set => SetProperty(ref _BillingHistoryList, value); }

    public DelegateCommand SortData { get; set; }
}

When I set a break point on ExecuteSort, it never gets there.

1

1 Answers

3
votes

In that DataGridTextColumn, the DataContext is going to be the billing history item (whatever's in BillingHistoryList), not the owner of the collection of billing history items. That's why {Binding ar_bill_dt} works: ar_bill_dt is surely a property of the billing history item.

If SortData is a property of the same viewmodel that owns BillingHistoryList, try this:

<DataGrid ItemsSource="{Binding BillingHistoryList}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding ar_bill_dt}">
            <DataGridTextColumn.Header>
                <Button 
                    Content="Bill Date" 
                    Command="{Binding DataContext.SortData, RelativeSource={RelativeSource AncestorType=DataGrid}}" 
                    />
            </DataGridTextColumn.Header>
        </DataGridTextColumn>
        <DataGridTextColumn Header="Amount" Binding="{Binding ar_orig_amt}" />
        <DataGridTextColumn Header="Running Total" Binding="{Binding RunningTotal}" />
    </DataGrid.Columns>
</DataGrid>

Another version. This is a more respectable way of doing it anyhow:

<DataGridTextColumn Binding="{Binding ar_bill_dt}">
    <DataGridTextColumn.HeaderTemplate>
        <DataTemplate>
            <Button 
                Content="Bill Date" 
                Command="{Binding DataContext.SortData, RelativeSource={RelativeSource AncestorType=DataGrid}}" 
                />
        </DataTemplate>
    </DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>