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

如何在Xamarin.Forms Shell中为两个页面设置同一ViewModel实例作为绑定上下文?

如何让两个XAML页面共享同一个ViewModel实例?

要实现两个页面共用SampleViewModel实例,保证购物车数据和商品状态双向同步,有以下几种可靠方案,按推荐程度排序:

1. 依赖注入(DI)(推荐)

现代XAML框架(如.NET MAUI、WPF配合Prism/Avalonia)都原生支持依赖注入,通过将ViewModel注册为单例,可以确保所有页面获取的是同一个实例。

步骤:

  1. 注册单例ViewModel
    在应用启动时的DI配置中(比如.NET MAUI的MauiProgram.cs),将SampleViewModel注册为单例:

    builder.Services.AddSingleton<SampleViewModel>();
    
  2. 在页面构造函数中注入
    主页面和购物车页面的构造函数接收注入的ViewModel实例,并设置为绑定上下文:

    // 主页面
    public MainPage(SampleViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
    
    // 购物车页面
    public CartPage(SampleViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
    

优势:

  • 符合现代架构设计,便于单元测试(可替换模拟实例)
  • 全局共享实例,无需手动传递
  • 框架自动管理实例生命周期

2. 静态单例模式

如果项目未使用DI,可以直接在ViewModel中实现静态单例,让所有页面引用同一个实例。

实现代码:

public class SampleViewModel : INotifyPropertyChanged
{
    // 静态单例实例(懒加载线程安全)
    private static readonly Lazy<SampleViewModel> _lazyInstance = 
        new Lazy<SampleViewModel>(() => new SampleViewModel());
    public static SampleViewModel Instance => _lazyInstance.Value;

    // 商品列表与购物车列表
    public ObservableCollection<Product> ProductList { get; } = new ObservableCollection<Product>();
    public ObservableCollection<Product> CartItemsList { get; } = new ObservableCollection<Product>();

    private int _cartItemsCount;
    public int CartItemsCount
    {
        get => _cartItemsCount;
        set
        {
            _cartItemsCount = value;
            OnPropertyChanged();
        }
    }

    // 加入购物车方法
    public void AddToCart(Product item)
    {
        CartItemsList.Add(item);
        item.HasAddedToCart = true;
        CartItemsCount++;
    }

    // 从购物车删除方法
    public void RemoveFromCart(Product item)
    {
        CartItemsList.Remove(item);
        item.HasAddedToCart = false;
        CartItemsCount--;
    }

    // INotifyPropertyChanged 实现
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// 商品实体需实现INotifyPropertyChanged以同步状态
public class Product : INotifyPropertyChanged
{
    private bool _hasAddedToCart;
    public bool HasAddedToCart
    {
        get => _hasAddedToCart;
        set
        {
            _hasAddedToCart = value;
            OnPropertyChanged();
        }
    }

    public string Name { get; set; }
    public decimal Price { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

页面绑定上下文设置:

可以在XAML中直接引用单例:

<!-- 主页面XAML -->
<ContentPage.BindingContext>
    <local:SampleViewModel Instance="{x:Static local:SampleViewModel.Instance}" />
</ContentPage.BindingContext>

或者在代码后台设置:

BindingContext = SampleViewModel.Instance;

注意:

  • 此方法简单直接,但不利于单元测试,单例实例全局唯一,难以替换模拟对象
  • 适合小型项目或快速原型开发

3. 导航时传递ViewModel实例

如果仅需在两个页面间共享实例,可以在导航时将主页面的ViewModel传递给购物车页面。

示例代码(以.NET MAUI为例):

  1. 主页面导航事件

    private async void NavigateToCart_Clicked(object sender, EventArgs e)
    {
        var currentVm = BindingContext as SampleViewModel;
        await Navigation.PushAsync(new CartPage(currentVm));
    }
    
  2. 购物车页面接收实例

    public CartPage(SampleViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
    

优势:

  • 无需全局共享,仅在需要的页面间传递
  • 灵活可控,适合临时场景

注意点:

  • 若后续有更多页面需要共享此实例,传递逻辑会变得繁琐

关键同步细节

无论使用哪种方案,确保以下两点以实现状态同步:

  • Product类必须实现INotifyPropertyChanged,修改HasAddedToCart时触发属性变更通知,主页面的按钮状态才会自动更新
  • CartItemsList使用ObservableCollection,添加/删除操作会自动通知UI刷新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 15:15:41