.NET MAUI ObservableCollection – UI wird nicht aktualisiert

Ich beschäftige mich aktuell mit einer Android-Anwendung, die ich mit .NET MAUI realisieren will. Da ich bereits viel mit .NET gearbeitet habe und in meiner beruflichen Laufbahn zahlreiche Windows-Anwendungen damit programmiert habe, schien mir das eine vernünftige Wahl. Doch .NET MAUI stellte mich mit der ObservableCollection vor eine unerwartete Herausforderung.

Mein Ziel war einfach: Ich wollte eine ContentPage erstellen, auf der Nutzer per Knopfdruck ein Bild mit der Kamera aufnehmen und dieses zusammen mit verschiedenen Tags in eine ObservableCollection speichern können. Warum ObservableCollection? Es handelt sich dabei um das gängige Verfahren in .NET, um sicherzustellen, dass Änderungen an Objekten direkt in der Benutzeroberfläche reflektiert werden. Beim Hinzufügen oder Entfernen von Items in dieser Collection sollte die Benutzeroberfläche automatisch aktualisiert werden. Soweit die Theorie.

Der ursprüngliche Code

In .NET MAUI funktioniert dies aber nicht wie erwartet. Es gibt einen entsprechenden Microsoft Learn-Artikel, in dem es funktioniert. Jedoch konnte ich es weder in .NET 6 noch in .NET 7 erfolgreich einsetzen. Die UI aktualisierte sich immer erst, wenn ich die App geschlossen und wieder geöffnet habe.

Hier ein Auszug aus meiner XAML-View:

<CollectionView x:Name="ClothingCollectionView" 
                            ItemsSource="{Binding ClothingItems}" 
                            VerticalOptions="FillAndExpand">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <Frame Padding="5" Margin="5" BorderColor="Gray" CornerRadius="5" BackgroundColor="Transparent">
                            <Image Source="{Binding SavedImagePath}" HeightRequest="150" BackgroundColor="Transparent">
                                <Image.GestureRecognizers>
                                    <TapGestureRecognizer Command="{Binding BindingContext.ImageTappedCommand, Source={x:Reference ClothingCollectionView}}" 
                                          CommandParameter="{Binding}" />
                                </Image.GestureRecognizers>
                            </Image>
                        </Frame>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </VerticalStackLayout>

Ich habe eine CollectionView, bei der die ItemsSource an ClothingItems gebunden ist. Im ItemTemplate werden Bilder basierend auf dem SavedImagePath-Binding angezeigt.

Hier ein kurzer Ausschnitt aus dem Code meines ViewModels mit dem Binding für ClothingItems. Zudem wird ein Element aus der ObservableCollection entfernt, was sich sofort in der UI widerspiegeln sollte – was ja nicht erfolgte.

public partial class MyWardrobeViewModel : ObservableObject
{

[ObservableProperty]
private ObservableCollection<ClothingItemModel> clothingItems;

private async void OnDeleteImageTapped(ClothingItemModel item)
{
    ClothingItems.Remove(item);
}
}
Anycubic Kobra Plus 3D Drucker mit Selbst Entwickeltm…*
  • 【Anycubic LeviQ Automatische 25-Punkt-Leveln】Der Dehnungsmessstreifen-Nivellierungssensor des…
  • 【Große Druckgröße und feiner Druck】300*300*350mm Druckgröße, um alle Ihre…

Letzte Aktualisierung am 22.05.2024 / Affiliate Links / Bilder von der Amazon Product Advertising API

Workaround als Lösung

Nach langer Recherche stieß ich auf dieses GitHub-Issue und stellte fest, dass es sich um einen seit längerem bekannten Fehler handelt. Das hat mich wirklich überrascht und verärgert. Ein solch grundlegendes Feature, wie die UI-Aktualisierung bei Objektänderungen, ist fehlerhaft und der Bug sogar als “verified” markiert. Na vielen Dank auch.

Zurzeit existieren nur Workarounds für diesen Fehler, die allerdings nicht mit dem MVVM-Konzept konform gehen.

Trotzdem wollte ich .NET MAUI und dessen ObservableCollection nicht sofort verwerfen. Daher setzte ich einen im GitHub-Issue beschriebenen Workaround um und definierte im Code-Behind ein Event. Dieses Event scrollt bei Auslösung an die Anfangsposition meiner CollectionView und lädt dadurch die Benutzeroberfläche neu. Dieses Event muss also jedes Mal ausgelöst werden, wenn Änderungen in der ObservableCollection vorgenommen werden, um diese Änderung auch sichtbar zu machen.

Hier der Code-Behind im Detail. Damit wird das Event abonniert:

public partial class MyWardrobeView : ContentPage
{
    public MyWardrobeView()
    {
        InitializeComponent();
        BindingContext = new MyWardrobeViewModel();
        (BindingContext as MyWardrobeViewModel).ScrollToTopRequested += OnScrollToTop;
    }

    private void OnScrollToTop(object sender, EventArgs e)
    {
        ClothingCollectionView.ScrollTo(0);
    }
}

Im ViewModel definiere ich nun das Event und löse es wie folgt aus (ScrollToTopRequested):

public partial class MyWardrobeViewModel : ObservableObject
{

public event EventHandler ScrollToTopRequested;

[ObservableProperty]
private ObservableCollection<ClothingItemModel> clothingItems;

private async void OnDeleteImageTapped(ClothingItemModel item)
{
    ClothingItems.Remove(item);
    ScrollToTopRequested?.Invoke(this, EventArgs.Empty);
}
}

Ich hoffe, dieser Artikel erspart euch die tagelange Fehlersuche. Schließlich habe ich bereits Tage damit verbracht und war verzweifelt.

Wenn ihr mehr über .NET MAU wissen wollt, dann holt euch doch das Buch Cross-Plattform-Apps mit .NET MAUI entwickeln*.

Ähnliche Beiträge

Beitrag teilen

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Cookie Consent Banner von Real Cookie Banner