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

如何在Xamarin Android与iOS中实现Community Toolkit MVVM的VM-UI绑定?

使用Community Toolkit MVVM实现原生Xamarin Android/iOS的VM-UI绑定

核心思路

Community Toolkit MVVM的ViewModel依然基于ObservableObject实现属性变更通知,无需让Activity/ViewController继承ObservableObject(这会打破UI层与VM层的分离原则)。通过在原生平台代码中手动订阅VM的PropertyChanged事件,即可实现UI对VM属性变化的响应。


Android 端实现步骤

1. 定义ViewModel

遵循Toolkit的规范实现VM,通过SetProperty触发属性变更通知:

using CommunityToolkit.Mvvm.ComponentModel;

public class MainViewModel : ObservableObject
{
    private string _greeting;
    public string Greeting
    {
        get => _greeting;
        set => SetProperty(ref _greeting, value);
    }

    public MainViewModel()
    {
        Greeting = "Hello from Community Toolkit MVVM";
    }
}

2. Activity中绑定VM与UI

在Activity中实例化VM,订阅其PropertyChanged事件并更新对应UI控件,注意在销毁时取消订阅避免内存泄漏:

using Android.App;
using Android.Widget;
using Android.OS;
using System.ComponentModel;

[Activity(Label = "XamarinAndroidMVVM", MainLauncher = true)]
public class MainActivity : Activity
{
    private MainViewModel _viewModel;
    private TextView _greetingTextView;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.Main);

        // 初始化VM与UI控件
        _viewModel = new MainViewModel();
        _greetingTextView = FindViewById<TextView>(Resource.Id.greetingTextView);

        // 订阅属性变更事件
        _viewModel.PropertyChanged += ViewModel_PropertyChanged;

        // 初始赋值
        UpdateGreetingText();

        // 模拟属性变化测试
        var changeBtn = FindViewById<Button>(Resource.Id.changeButton);
        changeBtn.Click += (s, e) => _viewModel.Greeting = "Updated Greeting!";
    }

    private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(MainViewModel.Greeting))
        {
            UpdateGreetingText();
        }
    }

    private void UpdateGreetingText()
    {
        // 确保在UI线程更新控件
        RunOnUiThread(() => _greetingTextView.Text = _viewModel.Greeting);
    }

    protected override void OnDestroy()
    {
        // 取消订阅
        _viewModel.PropertyChanged -= ViewModel_PropertyChanged;
        base.OnDestroy();
    }
}

iOS 端实现步骤

1. 复用ViewModel

直接使用Android端定义的MainViewModel即可。

2. ViewController中绑定VM与UI

逻辑与Android一致,在ViewController中订阅VM的属性变更事件,主线程更新UI:

using UIKit;
using Foundation;
using System.ComponentModel;

public partial class ViewController : UIViewController
{
    private MainViewModel _viewModel;

    public ViewController(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        // 初始化VM与UI控件
        _viewModel = new MainViewModel();
        greetingLabel.Text = _viewModel.Greeting;

        // 订阅属性变更事件
        _viewModel.PropertyChanged += ViewModel_PropertyChanged;

        // 模拟属性变化测试
        changeButton.TouchUpInside += (s, e) => _viewModel.Greeting = "Updated from iOS!";
    }

    private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(MainViewModel.Greeting))
        {
            // 主线程更新UI
            InvokeOnMainThread(() => greetingLabel.Text = _viewModel.Greeting);
        }
    }

    public override void ViewWillDisappear(bool animated)
    {
        // 取消订阅
        _viewModel.PropertyChanged -= ViewModel_PropertyChanged;
        base.ViewWillDisappear(animated);
    }
}

优化:封装绑定助手

如果手动订阅事件过于繁琐,可封装通用绑定工具类简化操作:

using System.ComponentModel;
using System.Reflection;

public static class BindingHelper
{
    public static void Bind<TViewModel, TControl>(
        TViewModel viewModel,
        string viewModelPropertyName,
        TControl control,
        Action<TControl, object> updateAction)
        where TViewModel : INotifyPropertyChanged
    {
        // 初始赋值
        var propInfo = typeof(TViewModel).GetProperty(viewModelPropertyName);
        if (propInfo != null)
        {
            updateAction(control, propInfo.GetValue(viewModel));
        }

        // 订阅属性变更
        viewModel.PropertyChanged += (s, e) =>
        {
            if (e.PropertyName == viewModelPropertyName)
            {
                updateAction(control, propInfo.GetValue(viewModel));
            }
        };
    }
}

Android中使用示例:

BindingHelper.Bind(_viewModel, nameof(MainViewModel.Greeting), _greetingTextView, (tv, value) =>
{
    RunOnUiThread(() => tv.Text = value.ToString());
});

iOS中使用示例:

BindingHelper.Bind(_viewModel, nameof(MainViewModel.Greeting), greetingLabel, (label, value) =>
{
    InvokeOnMainThread(() => label.Text = value.ToString());
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:37:24