Xamarin Forms模态页操作后ListView不更新问题求助
问题原因分析
核心问题是模态页的DataEditViewModel与主页面的DataViewModel是完全独立的实例:
- 主页面ListView绑定的是自身BindingContext中的
Coordinates集合 - 模态页的
DataEditViewModel继承自DataViewModel时,会重新执行LoadItems()创建全新的Coordinates集合,你在模态页中修改的是这个新集合,和主页面的集合没有任何关联 - 删除操作有效是因为删除命令直接绑定到了主页面ViewModel的实例,操作的是主页面的目标集合
解决方案
以下提供两种可行的修复方案,可根据项目架构选择:
方案1:共享ViewModel实例(最直接高效)
让模态页复用主页面的ViewModel实例,直接操作主页面的集合:
1. 修改DataEditViewModel
不再继承DataViewModel,改为接收主ViewModel的引用:
public class DataEditViewModel : BindableObject, INotifyPropertyChanged { private readonly DataViewModel _mainViewModel; private readonly DataStoreManager _dataStoreManager; public DataItem Item { get; set; } = new DataItem(); public bool IsNewAction { get; set; } public bool IsEditAction { get; set; } public DataEditViewModel(DataViewModel mainViewModel) { _mainViewModel = mainViewModel; _dataStoreManager = DataStoreManager.GetInstance(); OnPropertyChanged(nameof(Item)); } public ICommand NewCommand => new Command<DataItem>((item) => { item.Type = DataType.Coordinate; _dataStoreManager.AddItem(item); Device.BeginInvokeOnMainThread(() => { // 直接操作主ViewModel的集合 _mainViewModel.Coordinates.Add(item); }); }); public ICommand CommitCommand => new Command<DataItem>((item) => { _dataStoreManager.UpdateItem(item); Device.BeginInvokeOnMainThread(() => { var existingItem = _mainViewModel.Coordinates.Single(i => i.Id == item.Id); var index = _mainViewModel.Coordinates.IndexOf(existingItem); // 替换元素保持列表位置不变,若DataItem实现INotifyPropertyChanged,直接修改属性即可 _mainViewModel.Coordinates[index] = item; }); }); public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 修改主页面打开模态页的代码
在OnNewButtonClicked和OnEditButtonClicked中传递主ViewModel实例:
private async void OnNewButtonClicked(object sender, EventArgs e) { var mainViewModel = (DataViewModel)coordinatesPage.BindingContext; var editViewModel = new DataEditViewModel(mainViewModel) { IsNewAction = true, IsEditAction = false }; var editPage = new DataEditPage(); editPage.BindingContext = editViewModel; await Navigation.PushModalAsync(editPage); } private async void OnEditButtonClicked(object sender, EventArgs e) { var menuItem = (MenuItem)sender; var targetItem = (DataItem)menuItem.CommandParameter; var mainViewModel = (DataViewModel)coordinatesPage.BindingContext; var editViewModel = new DataEditViewModel(mainViewModel) { Item = targetItem, IsNewAction = false, IsEditAction = true }; var editPage = new DataEditPage(); editPage.BindingContext = editViewModel; await Navigation.PushModalAsync(editPage); }
3. 修改模态页XAML
移除XAML中自动创建ViewModel的代码,改为后台绑定:
<!-- 移除以下内容 --> <!-- <ContentPage.BindingContext> <viewmodels:DataEditViewModel /> </ContentPage.BindingContext> -->
方案2:使用MessagingCenter跨页面通知(解耦场景)
如果不想直接传递ViewModel实例,可通过MessagingCenter发送更新通知,主页面ViewModel监听后更新集合:
1. 主ViewModel注册消息
public DataViewModel() { LoadItems(); // 注册新增坐标的消息监听 MessagingCenter.Subscribe<DataEditViewModel, DataItem>(this, "AddCoordinate", (sender, item) => { Device.BeginInvokeOnMainThread(() => Coordinates.Add(item)); }); // 注册更新坐标的消息监听 MessagingCenter.Subscribe<DataEditViewModel, DataItem>(this, "UpdateCoordinate", (sender, item) => { Device.BeginInvokeOnMainThread(() => { var existingItem = Coordinates.Single(i => i.Id == item.Id); var index = Coordinates.IndexOf(existingItem); Coordinates[index] = item; }); }); } // 销毁时取消订阅避免内存泄漏 ~DataViewModel() { MessagingCenter.Unsubscribe<DataEditViewModel, DataItem>(this, "AddCoordinate"); MessagingCenter.Unsubscribe<DataEditViewModel, DataItem>(this, "UpdateCoordinate"); }
2. 修改DataEditViewModel的命令
发送消息而非直接操作集合:
public ICommand NewCommand => new Command<DataItem>((item) => { item.Type = DataType.Coordinate; _dataStoreManager.AddItem(item); MessagingCenter.Send(this, "AddCoordinate", item); }); public ICommand CommitCommand => new Command<DataItem>((item) => { _dataStoreManager.UpdateItem(item); MessagingCenter.Send(this, "UpdateCoordinate", item); });
额外优化点
ObservableCollection在Add/Remove/Replace元素时会自动触发PropertyChanged,无需手动调用OnPropertyChanged(nameof(Coordinates)),删除操作中的手动调用可移除- 若
DataItem实现了INotifyPropertyChanged,更新现有项时无需删除再添加,直接修改属性即可自动刷新列表 - 模态页按钮同时绑定了
Command和Clicked事件,建议仅保留Command避免重复触发
内容的提问来源于stack exchange,提问作者Craig85
相关产品推荐
相关产品推荐

