如何在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
相关产品推荐
相关产品推荐

