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:
Your issues stem from three key gaps in how Realm objects interact with Xamarin.Forms' binding system and ListView behavior:
- Realm nested collection change notifications aren't reaching the UI: Realm's
IList<AttendanceStudent>is a lazy-loaded collection, and direct bindings likeStudents[0].CheckOffReasonIDdon't subscribe to changes in nested objects. The baseList<AttendanceClass>also doesn't fire collection change events. - Command binding instability with ListView recycling: Using
x:Referencefor command binding can lead to context mismatches whenRecycleElementis enabled, and in some cases, GC can clean up references that break the command link. - 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.
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 }
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

