You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ListView嵌套对象疑似被GC回收,属性变更未通知UI

Hey, let's break down why your button stops working on Android over time and doesn't respond at all on iOS, then fix these issues step by step:

Problem Root Causes

Your issues stem from three key gaps in how Realm objects interact with Xamarin.Forms' binding system and ListView behavior:

  1. Realm nested collection change notifications aren't reaching the UI: Realm's IList<AttendanceStudent> is a lazy-loaded collection, and direct bindings like Students[0].CheckOffReasonID don't subscribe to changes in nested objects. The base List<AttendanceClass> also doesn't fire collection change events.
  2. Command binding instability with ListView recycling: Using x:Reference for command binding can lead to context mismatches when RecycleElement is enabled, and in some cases, GC can clean up references that break the command link.
  3. Direct index binding is fragile: Binding to Students[0] doesn't handle empty collections gracefully, and Realm won't trigger updates when that indexed object's properties change.

Step-by-Step Fixes

1. Fix Realm Object Change Notifications

Realm's RealmObject implements INotifyPropertyChanged, but nested collections and their elements don't automatically trigger parent object updates. We need to forward these events manually:

Update AttendanceClass to propagate changes

public class AttendanceClass : RealmObject, INotifyPropertyChanged
{
    public IList<AttendanceStudent> Students { get; }

    // Add a computed property for the first student's status
    public int FirstStudentCheckOffID
    {
        get => Students?.FirstOrDefault()?.CheckOffReasonID ?? 0;
    }

    protected override void OnManaged()
    {
        // Listen for changes to the Students collection
        ((INotifyCollectionChanged)Students).CollectionChanged += OnStudentsCollectionChanged;
        
        // Listen for property changes on existing students
        foreach (var student in Students)
        {
            student.PropertyChanged += OnStudentPropertyChanged;
        }
    }

    private void OnStudentsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // Subscribe to new students, unsubscribe from removed ones
        if (e.NewItems != null)
        {
            foreach (AttendanceStudent newStudent in e.NewItems)
                newStudent.PropertyChanged += OnStudentPropertyChanged;
        }
        if (e.OldItems != null)
        {
            foreach (AttendanceStudent oldStudent in e.OldItems)
                oldStudent.PropertyChanged -= OnStudentPropertyChanged;
        }

        // Trigger update for our computed property
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FirstStudentCheckOffID)));
    }

    private void OnStudentPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // If the CheckOffReasonID changes, update our computed property
        if (e.PropertyName == nameof(AttendanceStudent.CheckOffReasonID))
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FirstStudentCheckOffID)));
        }
    }
}

Replace List<AttendanceClass> with ObservableCollection

The base List doesn't notify the ListView of collection changes. Switch to an observable collection:

private ObservableCollection<AttendanceClass> _Classes; 
public ObservableCollection<AttendanceClass> Classes { 
    get { return _Classes; } 
    set { SetProperty(ref _Classes, value); } 
}

// When loading data, convert Realm results to ObservableCollection
var realmClasses = RealmHelper.Database.All<AttendanceClass>().ToList();
Classes = new ObservableCollection<AttendanceClass>(realmClasses);

2. Fix Command Binding Stability

Using RelativeSource instead of x:Reference is more reliable with ListView's recycling behavior, as it explicitly targets the ListView's binding context (your ViewModel):

<Button Text="Toggle" 
        Command="{Binding BindingContext.TogglePresentCommand, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
        CommandParameter="{Binding .}"/>

3. Update XAML Binding to Use the Computed Property

Replace the direct index binding with our new computed property to ensure UI updates trigger correctly:

<Label FontSize="7" Text="{Binding FirstStudentCheckOffID}"/>

4. Ensure Realm Instance Lifecycle is Stable

Keep a reference to your Realm instance in the ViewModel to prevent premature GC of Realm objects:

private Realm _realm;

public YourViewModel()
{
    _realm = RealmHelper.Database; // Use your existing Realm instance
}

Validation

After applying these changes:

  • Test iOS button clicks: the label should update immediately when you tap Toggle
  • Test Android over time: the button should remain responsive without GC-related failures
  • Verify edge cases (empty Students lists) don't cause crashes

内容的提问来源于stack exchange,提问作者Munk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:39:35