AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
26.15k stars 2.27k forks source link

Binding not updated when updating Collection using Dispatcher #17082

Closed julian-baumann closed 2 months ago

julian-baumann commented 2 months ago

Describe the bug

I am using the DataGrid component and Avalonia Version 11.1.3. Using a ViewModel I defined a Property Entries where I bind the ItemsSource to. I am calling a method using a Command, that updates this collection. When doing it in a synchronous environment everything works just fine, but as soon as I used Task.Run to run the logic and updated it using Dispatcher.UIThread.Invoke, nothing happens. No exception is thrown, and no UI updates in the DataGrid.

using ReactiveUI.SourceGenerators;

[Reactive] private ObservableCollection<Dictionary<string, string>> _entries = [];

public void GetEntries()
{
    Task.Run(() =>
    {
        Dispatcher.UIThread.Invoke(() =>
        {
            Entries = [];
        });

        ... Doing some stuff

        Dispatcher.UIThread.Invoke(() =>
        {
            Entries.Add(properties);
        });
    });
}

To Reproduce

Using DataGrid, Bindings and a ReactiveUI ViewModel

Expected behavior

No response

Avalonia version

11.1.3

OS

macOS

Additional context

No response

timunie commented 2 months ago

Dispatcher.UiTread.Post should be used iirc. Can you try this?

timunie commented 2 months ago

One more hint, you can try DynamicData if you need async manipulations. It's shipped with ReactiveUI anyways

julian-baumann commented 2 months ago

Both approaches don't work. Neither Dispatcher.UiTread.Post nor using SourceList with an ReadOnlyObservableCollection updates the UI

timunie commented 2 months ago
    [RelayCommand]
    private async Task LoadPeopleAsync()
    {
        await Task.Delay(1000);
        People = new ObservableCollection<Person>();

        await Task.Delay(1000);

        People.Add(new Person { FirstName = "John", LastName = "Doe" });
        People.Add(new Person { FirstName = "Jane", LastName = "Doe" });
        await Task.Delay(1000);
        People.Add(new Person { FirstName = "Julie", LastName = "Doe" });

        Dispatcher.UIThread.Invoke(() =>
        {
            People.Add(new Person { FirstName = "Norman", LastName = "Doe" });
        });
    }

Just tried it on my own and all of the above works. Note: I use a Command here to bind it to a button, but this is not required.

I see that you don't even await your Task, so maybe this is the issue. In any case, I think it is something in your code that blocks the UI: So I make it a Q&A for now.