如何通过QueryProperty在XAML页面间传递TimeSpan并解决转换异常?
解决System.InvalidCastException: 无法将'System.String'转换为'System.TimeSpan'的问题
错误原因
使用Xamarin.Forms Shell的GoToAsync传递导航参数时,所有参数都会被序列化为字符串类型。而你在目标页面ViewModel中直接用[QueryProperty]绑定TimeSpan类型的属性,Shell的参数绑定机制无法自动完成字符串到TimeSpan的转换,因此抛出类型转换异常。
解决方案1:使用中间字符串属性转换
在目标ViewModel中添加一个字符串类型的中间属性,用来接收查询参数,再手动转换为TimeSpan赋值给目标属性:
修改第二个页面的ViewModel
using System; using System.ComponentModel; using Xamarin.Forms; namespace TestApp2.ViewModels { // 绑定到中间字符串属性 [QueryProperty(nameof(TimeString), nameof(TransferDateTimePageViewModel.Time))] public class TransferDateTimePageViewModel : BaseViewModel, INotifyPropertyChanged { private TimeSpan time; public TimeSpan Time { get => time; set { time = value; OnPropertyChanged(); } } // 接收字符串参数的中间属性 private string timeString; public string TimeString { get => timeString; set { timeString = value; // 安全解析字符串为TimeSpan if (TimeSpan.TryParse(timeString, out TimeSpan parsedTime)) { Time = parsedTime; } OnPropertyChanged(); } } public TransferDateTimePageViewModel() { } } }
优化第一个页面的跳转代码(可选但推荐)
为了确保TimeSpan的字符串格式可被正确解析,使用标准格式字符串传递参数:
async void OnTimePickerSelected() { // 使用TimeSpan的标准格式字符串("c"表示ISO 8601格式) await Shell.Current.GoToAsync($"{nameof(TransferDateTimePage)}?" + $"{nameof(TransferDateTimePageViewModel.Time)}={Time.ToString("c")}"); }
注:原代码中的if (time != null)可以删除,因为TimeSpan是值类型,永远不会为null。
解决方案2:实现IQueryAttributable接口手动处理参数
通过实现IQueryAttributable接口,直接在ViewModel中处理导航查询参数,无需依赖[QueryProperty]特性:
修改第二个页面的ViewModel
using System; using System.Collections.Generic; using System.ComponentModel; using Xamarin.Forms; namespace TestApp2.ViewModels { public class TransferDateTimePageViewModel : BaseViewModel, INotifyPropertyChanged, IQueryAttributable { private TimeSpan time; public TimeSpan Time { get => time; set { time = value; OnPropertyChanged(); } } public TransferDateTimePageViewModel() { } // 实现接口方法,手动解析查询参数 public void ApplyQueryAttributes(IDictionary<string, string> query) { if (query.TryGetValue(nameof(Time), out string timeString)) { if (TimeSpan.TryParse(timeString, out TimeSpan parsedTime)) { Time = parsedTime; } } } } }
此方案下可以删除ViewModel上的[QueryProperty]特性。
效果验证
修改完成后,从第一个页面跳转时,TimeSpan会被序列化为字符串传递,目标页面ViewModel会将字符串解析回TimeSpan并绑定到TimePicker,即可正常显示和编辑时间,不会再抛出类型转换异常。
内容的提问来源于stack exchange,提问作者zorama
相关产品推荐
相关产品推荐

