prudentcoder
prudentcoder

Reputation: 371

Updating a property inside a LINQ clause breaks databinding to UI

I have a problem where a Silverlight UI datagrid is not updating, when the underlying data property changes. I can make it work if I introduce a temp variable and update the data in one chunk, but do not understand why the original code does not work.

This is the xaml code of the web UI part:

<C1:C1DataGrid ItemsSource="{Binding DeliveryAccuracyTotals}" 
               HeadersVisibility="Column" 
               AutoGenerateColumns="False">
    <C1:C1DataGrid.Columns>
        <C1:DataGridDateTimeColumn Binding="{Binding PlannedAcceptanceDate}" 
                                   Header="{Binding Date, 
                                   Source={StaticResource res}}" 
                                   Format="dd.MM.yyyy" />
        <C1:DataGridNumericColumn Binding="{Binding OKcount}" 
                                  Header="{Binding OK, Source={StaticResource res}}" />
        <C1:DataGridNumericColumn Binding="{Binding NOKcount}" 
                                  Header="{Binding NOK, Source={StaticResource res}}" />
        <C1:DataGridNumericColumn Binding="{Binding OKpercentage}" 
                                  Header="%" Format="0.##"/>
    </C1:C1DataGrid.Columns>
</C1:C1DataGrid>

I have a callback handler for handling the data when the async data fetch finishes:

private void handleDeliveryAccuracyReportDataCallBack(List<DeliveryAccuracyReportDataRow> data)
{
    DeliveryAccuracyTotals.Clear();
    if (data != null)
    {
        data.GroupBy(row => row.PlannedAcceptanceDate)
            .ToList()
            .ForEach(group => 
            DeliveryAccuracyTotals.Add(new DeliveryAccuracyTotalData(group.ToList())));
    }
}

This uses the following property to inform the UI of changes.

public List<DeliveryAccuracyTotalData> DeliveryAccuracyTotals
{
    get
    {
        if (_deliveryAccuracytotals == null)
        {
            _deliveryAccuracytotals = new List<DeliveryAccuracyTotalData>();
        }
        return _deliveryAccuracytotals;
    }
    private set
    {
        _deliveryAccuracytotals = value;
        NotifyPropertyChanged("DeliveryAccuracyTotals");
    }
}

If I run this, the DeliveryAccuracyTotals property gets populated properly with the new data but the UI does not show the new data!

However, when I change the callback code to this, it works:

private void handleDeliveryAccuracyReportDataCallBack(List<DeliveryAccuracyReportDataRow> data)
{
    var temp = new List<DeliveryAccuracyTotalData>();
    if (data != null)
    {
        data.GroupBy(row => row.PlannedAcceptanceDate)
        .ToList()
        .ForEach(group => 
        temp.Add(new DeliveryAccuracyTotalData(group.ToList())));
    }
    DeliveryAccuracyTotals = temp;
}

Can anyone explain what is going on in here? I suspect it has something to do with the ForEach() and/or closures.

Upvotes: 1

Views: 72

Answers (2)

Aron
Aron

Reputation: 15772

Although Servy is correct is his analysis I would say his advice is not the best. Observable collection would not fix your problem for you. Also rebinding a new collection each time will reset the state of your tree view (bad UX).

You should look into the various PropertyChanged aware Linq query implementations. Such as, but not limited to

  • Binding Linq
  • Continuous Linq
  • ReactiveUI's derived observable collection

I would suggest RxUI

Upvotes: 1

Servy
Servy

Reputation: 203834

You only fire NotifyPropertyChanged when you set a new value to the DeliveryAccuracyTotals field. You don't fire the event when the list is mutated, only when an entirely new list is set.

Were the property to be an ObservableCollection then the UI would be able to add a handler to pick up notifications of mutating to the collection itself, and not just changes to the variable holding the list, but if you're going to completely re-create the collection from scratch anyway, rather than just make a few changes, it's not really a problem to just assign a new collection to that property.

Upvotes: 2

Related Questions