0

could you please help me to understand how to resolve issue of the UI being slow responsive while updating ObservableCollection. The problem is that I'm processing 50K lines file and all works very well untill I start adding these items to the ViewModel's source observable collection.

Even with the fastest possible way it still works very slow

var enumerator = service.SeedCSVFileDataAsync(csvFilePath);

   
    await foreach (var item in enumerator.WithCancellation(CancellationToken.None))
    {
        userObjects.Add(item);
        Source.Add(item); // Here is where the slow begin

    }

I understand that adding 50k items to the observable collection generates tons of events which is the root cause of slow response issue however when I'm tryinng to cheat this issue by re-initialize observablecollection with pre-populated collection it stops recieve any events at all.

Source = new ObservableCollection<ADUserObject>(userObjects); after this no data on the screen even if you try to add anything

I think this is because of issues in the generated code and architecture but I can't figure out this by my self, so maybe someone already resolved this issue.

I would be very appreciate you'll if you could help me to understand how to resolve this issue. Again the 50k lines file reads and populates IEnumerable collection with in the few milliseconds with out blocking the ui, the problem appears only when I'm start working with the ObservableCollection.

Appreciate your response in advance, Best regards, Maks.

EDit BTW I just realized that most likely the reason of why this trick isn't work

Source = new ObservableCollection<ADUserObject>(userObjects);

is beacuse compiler do it's own magick with *.g.cs files based on on the framework settings ... did anyone faced already this problem ?

Max Zaikin
  • 65
  • 9
  • There should be a clear distinction between freezing and not showing data. Does you application seize up and won't respond or is the view just not getting updated ? – CodeJunkie Jun 08 '23 at 11:41
  • it is showing data but UI thread slow responsing while populating ObservableCollection prop due to the reason of generating tons of events. – Max Zaikin Jun 08 '23 at 13:07

2 Answers2

0

Are you calling PropertyChanged(this, new PropertyChangedEventArgs("Source")); from INotifyPropertyChanged after setting the Source variable?

An observable collection notifies the UI when its contents are altered, but if the reference to the observable collecition itself is changed no notification is send by default.

Consider adding the following code to never forget notifying the UI.

private ObservableCollection<T> _source;
public ObservableCollection<T> Source
{
    get => _source;
    set
    {
        if (value != _source)
        {
            _source = value;
            PropertyChanged(this, new PropertyChangedEventArgs("Source"));
        }
    }
}
Vinxian
  • 9
  • 2
  • The usual practive for ObservableCollection is to create once and then add / remove items as required, so this is irrelevant. – Peregrine Jun 08 '23 at 12:28
  • Agree with Peregrine, besides that proposed solution isn't applicable for this case as code has been initially generated by the WinUI Template Studio. This is why it is also confusing me as normaly thouse my cheat would be applicable and work however in generated framework it is not... – Max Zaikin Jun 08 '23 at 12:41
0

Alright, problem solved. Thank's to Christian Nagel for good thouds given in his articleWhat’s the SynchronizationContext used for? and Filip Ekberg for great course at Pluralsight "Asynchronous Programming in C#10"

Here is the raw snippet which is smoothly loading 50k lines in the data grid without frozing the UI thread (pls don't judge me too hard, I know it need some refactoring...)

/// <summary>
/// [MZ]
/// </summary>
/// <param name="parameter"></param>
public async Task<IEnumerable<T>> ProcessCSVData(string csvFilePath, CSVDataService service)
{
    
    Microsoft.UI.Dispatching.DispatcherQueue dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();

    List<T> _mObjects = new List<T>();

    Source.Clear();        

    await  Task.Run(async () => {

        var enumerator = service.SeedCSVFileDataAsync(csvFilePath);

        await foreach (var item in enumerator.WithCancellation(CancellationToken.None))
        {
            _mObjects.Add(item);
            //Source.Add(item);

            await dispatcherQueue.EnqueueAsync(() =>
            {
                Source.Add(item);
            });
        }

    });
    return _mObjects;
}

Appreciate your answers and participation in this problem, Best regards, Maks.

P.S. I also have open an issue on the GitHub, just to make sure that this isn't a bug for thous who are interested, here is the link: UI Thread slow response due to the populating of 50k records file in ViewModel.Source observable collection #4688

Max Zaikin
  • 65
  • 9