MAUI中Picker+动态CollectionView实现货币转换器无数据显示问题
.NET MAUI CollectionView不显示数据+货币切换同步更新问题修复
核心问题分析
你代码里的几个关键错误导致了CollectionView不显示数据,同时货币切换功能完全失效:
- 绑定错误:CollectionView的
ItemsSource绑定写成了{Binding .},而且Income的绑定错误指向了方法名OnPickerSelectedIndexChanged,应该绑定MonthlyIncome的Income属性。 - 事件未触发:你在
Calculate方法内部定义了OnPickerSelectedIndexChanged,但根本没给Picker的SelectedIndexChanged事件绑定这个方法,逻辑位置完全错误。 - 无属性变更通知:
MonthlyIncome类没实现INotifyPropertyChanged,就算修改Income值,UI也不会自动更新。 - 验证逻辑位置错误:把输入验证放在for循环内部,每循环一次就检查一遍,会重复弹出提示。
修正后的XAML代码
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BankingApp.HomePage" xmlns:res="clr-namespace:BankingApp.Resources" Title="HomePage" BackgroundColor="#041014" Shell.NavBarIsVisible="False" NavigationPage.HasNavigationBar="False" NavigationPage.HasBackButton="False"> <ScrollView> <VerticalStackLayout> <!--Logo--> <Image Source="logo.png" Margin="0,30,0,0" HorizontalOptions="Center" HeightRequest="100"></Image> <Label FontSize="16" HorizontalOptions="Center" Text="{x:Static res:LanguageResources.Title}" FontFamily="AgeoBold"></Label> <Label FontSize="20" HorizontalOptions="Center" Text="10%"></Label> <!--Currency Picker - 添加SelectedIndexChanged事件绑定--> <Picker x:Name="CurrencyInUse" Margin="0,0,20,0" FontSize="16" Title="Choose your Currency" HorizontalTextAlignment="End" HorizontalOptions="End" SelectedIndexChanged="OnPickerSelectedIndexChanged"> <Picker.ItemsSource> <x:Array Type="{x:Type x:String}"> <x:String>Euro</x:String> <x:String>Dollar</x:String> <x:String>Croatian Kuna</x:String> </x:Array> </Picker.ItemsSource> </Picker> <Frame Margin="10" BackgroundColor="#16181C" HasShadow="True" Padding="20" CornerRadius="20"> <StackLayout> <Label HorizontalOptions="CenterAndExpand" FontFamily="AgeoBold" TextColor="White" FontSize="18" Text="{x:Static res:LanguageResources.InitialInvestment}"/> <Grid Margin="0.5"> <Frame Margin="15" HorizontalOptions="CenterAndExpand" BackgroundColor="#202327" WidthRequest="150" CornerRadius="30" Padding="0" > <Entry Keyboard="Numeric" x:Name="IncomeInput" BackgroundColor="#202327" TextColor="#0d1a21" FontFamily="OpenSansRegular" FontSize="16" Placeholder="{x:Static res:LanguageResources.HowMuch}" HorizontalOptions="CenterAndExpand" PlaceholderColor="#202327" /> </Frame> </Grid> <Label HorizontalOptions="CenterAndExpand" FontFamily="AgeoBold" TextColor="White" FontSize="18" Text="{x:Static res:LanguageResources.Duration}"/> <Grid Margin="0.5"> <Frame Opacity="1" Margin="10" HorizontalOptions="CenterAndExpand" BackgroundColor="#202327" WidthRequest="150" CornerRadius="30" Padding="0" > <Entry Keyboard="Numeric" x:Name="MonthInput" BackgroundColor="#202327" TextColor="#0d1a21" FontFamily="OpenSansRegular" FontSize="16" Placeholder="{x:Static res:LanguageResources.HowLong}" HorizontalOptions="CenterAndExpand" PlaceholderColor="#202327"/> </Frame> </Grid> <Button HorizontalOptions="Center" Margin="10" WidthRequest="150" HeightRequest="40" BackgroundColor="#00B2FF" Text="{x:Static res:LanguageResources.Calculate}" TextColor="White" FontFamily="AgeoBold" Clicked="Calculate"/> </StackLayout> </Frame> <Label FontSize="16" HorizontalOptions="Center" Text="{x:Static res:LanguageResources.Results}" FontFamily="AgeoBold"></Label> <!--修正CollectionView绑定,ItemTemplate绑定到Income属性并格式化--> <CollectionView ItemsSource="{Binding MonthlyIncomeCalculus}"> <CollectionView.ItemTemplate> <DataTemplate> <VerticalStackLayout Margin="10" BackgroundColor="#202327" Padding="10"> <Label Text="{x:Static res:LanguageResources.Month}" FontFamily="AgeoBold" /> <Label Text="{Binding Month}" FontAttributes="Bold" /> <Label Text="{x:Static res:LanguageResources.IncomewithRate}" FontFamily="AgeoBold" /> <Label Text="{Binding Income, StringFormat='{0:F2}'}" FontAttributes="Bold"/> </VerticalStackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </VerticalStackLayout> </ScrollView> </ContentPage>
修正后的C#代码
namespace BankingApp; using System.ComponentModel; using System.Runtime.CompilerServices; public partial class HomePage : ContentPage { // 公开集合属性供XAML绑定 public ObservableCollection<MonthlyIncome> MonthlyIncomeCalculus { get; set; } = new(); // 保存原始欧元计算结果,切换货币时直接转换无需重复计算复利 private List<MonthlyIncome> _originalCalculations = new(); private int _originalIncome; private int _originalMonths; public HomePage() { InitializeComponent(); // 设置页面绑定上下文为自身,让XAML能访问页面属性 BindingContext = this; } // 实现INotifyPropertyChanged,确保属性变更时UI自动更新 public class MonthlyIncome : INotifyPropertyChanged { private int _month; private double _income; public int Month { get => _month; set { _month = value; OnPropertyChanged(); } } public double Income { get => _income; set { _income = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } private void Calculate(object sender, EventArgs e) { // 输入验证移到循环前,避免重复提示 if (string.IsNullOrEmpty(MonthInput.Text) || string.IsNullOrEmpty(IncomeInput.Text)) { DisplayAlert("Attention", "Please input values!", "Ok"); return; } if (!int.TryParse(MonthInput.Text, out int months) || months < 1 || months > 12) { DisplayAlert("Attention", "Please input value of range 1-12", "Ok"); return; } if (!int.TryParse(IncomeInput.Text, out int income) || income < 10) { DisplayAlert("Attention", "Please input minimum value of 10", "Ok"); return; } // 保存原始参数和计算结果 _originalIncome = income; _originalMonths = months; _originalCalculations.Clear(); MonthlyIncomeCalculus.Clear(); double accumulated = 0; for (int i = 1; i <= months; i++) { var monthItem = new MonthlyIncome(); monthItem.Month = i; // 复利计算逻辑 if (i == 1) { monthItem.Income = income * 1.10; accumulated = monthItem.Income; } else { monthItem.Income = accumulated * 1.10; accumulated = monthItem.Income; } monthItem.Income = Math.Round(monthItem.Income, 2); // 保存原始欧元结果 _originalCalculations.Add(new MonthlyIncome { Month = i, Income = monthItem.Income }); // 应用当前选中货币的转换 ApplyCurrencyConversion(monthItem); MonthlyIncomeCalculus.Add(monthItem); } } // 提取货币转换逻辑,复用性更高 private void ApplyCurrencyConversion(MonthlyIncome item) { double conversionRate = CurrencyInUse.SelectedIndex switch { 1 => 1.05, // Dollar 2 => 7.55, // Croatian Kuna _ => 1.0 // Euro(默认不转换) }; item.Income = Math.Round(item.Income / conversionRate, 2); } // Picker切换事件:重新转换所有显示项 private void OnPickerSelectedIndexChanged(object sender, EventArgs e) { if (_originalCalculations.Count == 0) return; for (int i = 0; i < MonthlyIncomeCalculus.Count; i++) { var originalItem = _originalCalculations[i]; var displayItem = MonthlyIncomeCalculus[i]; // 用原始欧元结果重新转换 displayItem.Income = Math.Round(originalItem.Income / GetConversionRate(), 2); } } // 获取当前选中货币的转换率 private double GetConversionRate() { return CurrencyInUse.SelectedIndex switch { 1 => 1.05, 2 => 7.55, _ => 1.0 }; } }
关键修改说明
- 绑定上下文设置:构造函数中设置
BindingContext = this;,让XAML能直接绑定页面的MonthlyIncomeCalculus集合。 - 属性变更通知:
MonthlyIncome实现INotifyPropertyChanged,确保修改Income时UI实时更新。 - 分离原始数据与显示数据:保存原始欧元计算结果,切换货币时只需转换无需重复计算复利,提升效率。
- 事件与逻辑分离:把货币转换逻辑单独提取,Picker事件触发时统一更新所有项,代码更清晰。
- 输入验证优化:用
int.TryParse避免格式崩溃,验证逻辑移到循环前,不会重复弹出提示。
内容的提问来源于stack exchange,提问作者Kimba
相关产品推荐
相关产品推荐

