You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
        };
    }
}

关键修改说明

  1. 绑定上下文设置:构造函数中设置BindingContext = this;,让XAML能直接绑定页面的MonthlyIncomeCalculus集合。
  2. 属性变更通知:MonthlyIncome实现INotifyPropertyChanged,确保修改Income时UI实时更新。
  3. 分离原始数据与显示数据:保存原始欧元计算结果,切换货币时只需转换无需重复计算复利,提升效率。
  4. 事件与逻辑分离:把货币转换逻辑单独提取,Picker事件触发时统一更新所有项,代码更清晰。
  5. 输入验证优化:用int.TryParse避免格式崩溃,验证逻辑移到循环前,不会重复弹出提示。

内容的提问来源于stack exchange,提问作者Kimba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 18:25:18