Xamarin Forms MVVM模式下UWP平台ListView绑定失效问题
让我们一步步排查你的问题,核心原因出在INotifyPropertyChanged的错误实现上,还有几个次要的绑定细节需要修正:
1. 致命错误:INotifyPropertyChanged事件被空实现覆盖
在你的ViewModel里,你显式实现了接口的PropertyChanged事件,但这个实现是空的:
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add { } remove { } }
这会导致页面绑定订阅的接口事件没有任何实际的订阅逻辑,即使你调用OnPropertyChanged("ObjPizzaList"),页面也接收不到任何通知——这就是ListView绑定失效的根本原因。
解决方法:直接删除这段代码,因为你已经定义了public的PropertyChanged事件,接口会自动使用这个公共事件。
2. 建议:用ObservableCollection替代List
虽然你是直接给ObjPizzaList赋值整个列表,但List<T>本身不会自动通知集合元素的变化(比如后续添加/删除项)。换成ObservableCollection<XmlPizzaDetails>更符合MVVM的规范,它会自动触发集合变化的通知:
修改ViewModel里的字段和属性:
// 替换List为ObservableCollection ObservableCollection<XmlPizzaDetails> _objPizzaList; public ObservableCollection<XmlPizzaDetails> ObjPizzaList { get { return _objPizzaList; } set { if (_objPizzaList != value) { _objPizzaList = value; // 用nameof代替硬编码字符串,避免拼写错误 OnPropertyChanged(nameof(ObjPizzaList)); } } }
然后在GetRequest里初始化:
ObjPizzaList = new ObservableCollection<XmlPizzaDetails>();
3. 修正属性的setter:触发PropertyChanged通知
你其他属性(比如Progress、CusButtonText)的setter没有调用OnPropertyChanged,这会导致这些属性的绑定也无法实时更新(比如你的ActivityIndicator可能不会显示加载状态,按钮文本修改后也不会更新)。
以Progress为例,修改setter:
bool _progress; public bool Progress { get { return _progress; } set { if (_progress != value) { _progress = value; OnPropertyChanged(nameof(Progress)); } } }
同样的,把CusButtonText、SelectedDescription等属性的setter都改成这种形式。
4. 优化异步方法:避免async void在构造函数中使用
你的GetRequest是async void方法,在构造函数中调用async void可能导致异常无法被捕获,建议改成async Task,然后在构造函数中用如下方式调用:
public ViewModel () { // 用_ = 忽略返回的Task,避免编译器警告 _ = GetRequest(); } // 修改方法签名为async Task public async Task GetRequest() { if (NetworkCheck.IsInternet()) { // 先设置Progress为true,显示加载指示器 Progress = true; Uri geturi = new Uri("http://api.androidhive.info/pizza/?format=xml"); HttpClient client = new HttpClient(); HttpResponseMessage responseGet = await client.GetAsync(geturi); string response = await responseGet.Content.ReadAsStringAsync(); //Xml Parsing ObjPizzaList = new ObservableCollection<XmlPizzaDetails>(); XDocument doc = XDocument.Parse(response); foreach (var item in doc.Descendants("item")) { XmlPizzaDetails ObjPizzaItem = new XmlPizzaDetails(); ObjPizzaItem.ID = item.Element("id").Value.ToString(); ObjPizzaItem.Name = item.Element("name").Value.ToString(); ObjPizzaItem.Cost = item.Element("cost").Value.ToString(); ObjPizzaItem.Description = item.Element("description").Value.ToString(); ObjPizzaList.Add(ObjPizzaItem); } Progress = false; } }
最后验证
做完这些修改后,你的ListView应该能正常绑定到ViewModel的ObjPizzaList了,同时其他控件的绑定也会正常工作。另外要确保你的XmlPizzaDetails类的属性都是public的(从你后台设置ItemSource有效来看,这个应该没问题)。
内容的提问来源于stack exchange,提问作者holdorfs

