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

Xamarin.Forms ListView持续创建ViewCells绑定同一ViewModel问题求助

问题描述

我编写了一个Xamarin.Forms的Demo,发现ListView会持续创建ViewCell,并且全部绑定到同一个ViewModel,最终会耗尽内存。运行后界面中的Cell Serial #和Pings数值会持续增长,即使列表只有一项也是如此;添加两项后,两个单元格的这两个数值也会不断增长。


相关代码

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="LVD.MainPage">
    <StackLayout>
        <Label Text="This is a listview with one item."/>
        <Label Text="{Binding MainNum}"/>
        <ListView x:Name="DemoList" 
                ItemsSource="{Binding Itms}"
                RowHeight="75"
                CachingStrategy="RecycleElement">
        </ListView>
    </StackLayout>
</ContentPage>

MainPage.xaml.cs

public partial class MainPage : ContentPage
{
    MainVM _vm = null;
    public MainPage()
    {
        InitializeComponent();

        _vm = new MainVM();
        BindingContext = _vm;

        DemoList.ItemTemplate = new DataTemplate(typeof(myCell));
    }
}

MainVM.cs

using System.Collections.ObjectModel;

namespace LVD
{
    class MainVM
    {
        public string MainNum { get; set; }
        private static int _mainserno = 0;
        public ObservableCollection<CellVM> Itms { get; set; }

        public MainVM()
        {
            MainNum = "Parent serial#: " + _mainserno.ToString();
            _mainserno++;
            if (Itms == null)
                Itms = new ObservableCollection<CellVM>();            
            else
                Itms.Clear();

            CellVM item = new CellVM();
            Itms.Add(item);
            //CellVM item2 = new CellVM();
            //Itms.Add(item2);
        }
    }
}

myCell.cs

using Xamarin.Forms;

namespace LVD
{
    class myCell : ViewCell
    {
        static int _localserialno = 0;
        Label VMnumLbl, CellNumLbl, PLbl;

        public static readonly BindableProperty ItemProperty =
            BindableProperty.Create("CellItem", typeof(CellVM), typeof(myCell), null);

        public CellVM CellItem
        {
            get { return (CellVM)GetValue(ItemProperty); }
            set { SetValue(ItemProperty, value); }
        }

        public myCell()
        {
            VMnumLbl = new Label();
            VMnumLbl.SetBinding(Label.TextProperty, "VMSerNum", BindingMode.Default, null, "VM Serial #: {0}");

            CellNumLbl = new Label();
            CellNumLbl.SetBinding(Label.TextProperty, "CellSerNum", BindingMode.Default, null, "Cell Serial #: {0}");

            PLbl = new Label();
            PLbl.SetBinding(Label.TextProperty, "PingCount", BindingMode.Default, null, "Pings: {0}");

            var stackit = new StackLayout();
            stackit.Children.Add(VMnumLbl);
            stackit.Children.Add(CellNumLbl);
            stackit.Children.Add(PLbl);
            View = stackit;
        }

        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();

            if (BindingContext == null)
                CellItem = null;
            else if (BindingContext != CellItem)
            {
                CellItem = (CellVM)BindingContext;
                CellItem.CellSerNum = _localserialno++;
                CellItem.Ping();
            }
        }
    }
}

CellVM.cs

class CellVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private static int serialno = 0;
    private int _vmserialno;
    public int VMSerNum { get { return _vmserialno; } }
    public int CellSerNum { get; set; }
    public int PingCount { get; set; }
    public CellVM()
    {
        _vmserialno = serialno++;
        PingCount = 0;
    }

    public void Ping()
    {
        PingCount++;
        OnPropertyChanged(string.Empty);
    }
}

运行现象

This is a listview with one item.
Parent serial #: 0
VM Serial #: 0
Cell Serial #: ++
Pings: ++

Cell Serial #和Pings数值持续增长。


问题原因及解决方法

核心原因

无限循环触发绑定上下文变更:

  1. 在myCell.OnBindingContextChanged中,设置CellSerNum并调用Ping();
  2. Ping()里调用OnPropertyChanged(string.Empty),这会触发ViewModel所有属性的变更通知,包括刚设置的CellSerNum;
  3. 属性变更会让ListView认为单元格需要更新,重新设置BindingContext,再次触发OnBindingContextChanged;
  4. 循环往复,导致CellSerNum和PingCount持续增长,ViewCell被反复绑定。

解决步骤

  1. 修改Ping()方法的属性通知:只触发PingCount的变更,而非所有属性
public void Ping()
{
    PingCount++;
    OnPropertyChanged(nameof(PingCount));
}
  1. 调整CellSerNum的设置位置:将其移到CellVM的构造函数中,避免在绑定上下文变更时重复赋值
class CellVM : INotifyPropertyChanged
{
    // 添加静态计数器
    private static int _cellSerCounter = 0;
    
    // ... 其他代码
    
    public CellVM()
    {
        _vmserialno = serialno++;
        PingCount = 0;
        CellSerNum = _cellSerCounter++; // 在这里设置单元格序列号
    }
    
    // ... 其他代码
}
  1. 移除myCell.OnBindingContextChanged中的冗余代码:删除CellItem.CellSerNum = _localserialno++;这一行

经过以上修改,即可打破无限循环,ViewCell不会被反复创建和绑定,数值也不会持续增长。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:01:20