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

MAUI组件中派生参数Progress未更新的原因及修复方案

MAUI折扣进度卡片组件:进度条不更新的问题排查与修复

问题场景

我正在开发一个MAUI组件DiscountProgressCard,用于展示用户完成行程的进度以获取折扣。组件的XAML和后台代码如下:

组件XAML(DiscountProgressCard.xaml)

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyProject.DiscountProgressCard"
             x:Name="this">

    <Border BindingContext="{x:Reference this}">  <!--Border styling cut for brevity -->
        <VerticalStackLayout>

            <ProgressBar ProgressColor="Green" Progress="{Binding Progress}" />

            <Label>
                <Label.Text>
                    <MultiBinding StringFormat="{}{0} out of {1} trips completed.">
                        <Binding Path="CompletedTrips" />
                        <Binding Path="TripsNecessaryForDiscount" />
                    </MultiBinding>
                </Label.Text>
            </Label>

        </VerticalStackLayout>
    </Border>
</ContentView>

组件后台代码(DiscountProgressCard.xaml.cs)

public partial class DiscountProgressCard : ContentView
{
    public DiscountProgressCard()
    {
        InitializeComponent();
    }

    // 用户已完成的行程数
    public int CompletedTrips
    {
        get { return (int)GetValue(CompletedTripsProperty); }
        set { SetValue(CompletedTripsProperty, value); }
    }

    public static readonly BindableProperty CompletedTripsProperty = BindableProperty.Create(
        nameof(CompletedTrips), 
        typeof(int), 
        typeof(DiscountProgressCard),
        0);

    // 获得折扣所需的行程数
    public int TripsNecessaryForDiscount
    {
        get { return (int)GetValue(TripsNecessaryForDiscountProperty); }
        set { SetValue(TripsNecessaryForDiscountProperty, value); }
    }

    public static readonly BindableProperty TripsNecessaryForDiscountProperty = BindableProperty.Create(
        nameof(TripsNecessaryForDiscount),
        typeof(int), 
        typeof(DiscountProgressCard),
        999);

    // 由两个属性派生的进度值,仅组件内部使用
    protected decimal Progress { 
        get { return Decimal.Divide(CompletedTrips, TripsNecessaryForDiscount); }
    }
}

组件使用示例(MyPage.xaml)

<ContentPage>
<!--...cut for brevity...-->
    <components:DiscountProgressCard CompletedTrips="200" TripsNecessaryForDiscount="400" />
</ContentPage>

现象:组件渲染后,标签能正确显示200 out of 400 trips completed.,但进度条始终为空。调试发现Progress的getter仅在组件初始化时执行一次,使用的是默认值(0/999≈0),后续传入的属性值变化不会触发重新计算。

问题原因

Progress是普通CLR属性,MAUI绑定系统无法感知它的依赖属性(CompletedTrips和TripsNecessaryForDiscount)的变化。绑定系统只会在初始化时读取一次Progress的值,之后即使依赖属性更新,也不会主动重新调用Progress的getter,导致进度条无法更新。

修复方案

方案一:通过绑定属性回调更新进度

给CompletedTrips和TripsNecessaryForDiscount的绑定属性添加propertyChanged回调,当这两个属性变化时,主动通知绑定系统更新Progress的值,同时将Progress改为可绑定属性:

修改后台代码如下:

public partial class DiscountProgressCard : ContentView
{
    public DiscountProgressCard()
    {
        InitializeComponent();
    }

    // 用户已完成的行程数
    public int CompletedTrips
    {
        get { return (int)GetValue(CompletedTripsProperty); }
        set { SetValue(CompletedTripsProperty, value); }
    }

    public static readonly BindableProperty CompletedTripsProperty = BindableProperty.Create(
        nameof(CompletedTrips), 
        typeof(int), 
        typeof(DiscountProgressCard),
        0,
        propertyChanged: OnProgressDependentPropertyChanged);

    // 获得折扣所需的行程数
    public int TripsNecessaryForDiscount
    {
        get { return (int)GetValue(TripsNecessaryForDiscountProperty); }
        set { SetValue(TripsNecessaryForDiscountProperty, value); }
    }

    public static readonly BindableProperty TripsNecessaryForDiscountProperty = BindableProperty.Create(
        nameof(TripsNecessaryForDiscount),
        typeof(int), 
        typeof(DiscountProgressCard),
        999,
        propertyChanged: OnProgressDependentPropertyChanged);

    // 进度值改为可绑定属性
    public double Progress
    {
        get { return (double)GetValue(ProgressProperty); }
        private set { SetValue(ProgressProperty, value); }
    }

    public static readonly BindableProperty ProgressProperty = BindableProperty.Create(
        nameof(Progress),
        typeof(double),
        typeof(DiscountProgressCard),
        0.0);

    // 依赖属性变化时更新Progress
    private static void OnProgressDependentPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is DiscountProgressCard card)
        {
            // 避免除以0的情况
            if (card.TripsNecessaryForDiscount == 0)
            {
                card.Progress = 0;
                return;
            }
            card.Progress = (double)Decimal.Divide(card.CompletedTrips, card.TripsNecessaryForDiscount);
        }
    }
}

方案二:在XAML中直接使用MultiBinding计算进度

无需在后台代码中定义Progress属性,直接在ProgressBar的Progress绑定上使用MultiBinding和转换器计算进度,更符合MAUI的绑定设计:

步骤1:创建进度计算转换器

public class TripProgressConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length != 2 || 
            !(values[0] is int completed) || 
            !(values[1] is int required))
        {
            return 0.0;
        }
        if (required == 0) return 0.0;
        return (double)Decimal.Divide(completed, required);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

步骤2:修改组件XAML

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyProject"
             x:Class="MyProject.DiscountProgressCard"
             x:Name="this">

    <ContentView.Resources>
        <local:TripProgressConverter x:Key="TripProgressConverter" />
    </ContentView.Resources>

    <Border BindingContext="{x:Reference this}">  <!--Border styling cut for brevity -->
        <VerticalStackLayout>

            <ProgressBar ProgressColor="Green">
                <ProgressBar.Progress>
                    <MultiBinding Converter="{StaticResource TripProgressConverter}">
                        <Binding Path="CompletedTrips" />
                        <Binding Path="TripsNecessaryForDiscount" />
                    </MultiBinding>
                </ProgressBar.Progress>
            </ProgressBar>

            <Label>
                <Label.Text>
                    <MultiBinding StringFormat="{}{0} out of {1} trips completed.">
                        <Binding Path="CompletedTrips" />
                        <Binding Path="TripsNecessaryForDiscount" />
                    </MultiBinding>
                </Label.Text>
            </Label>

        </VerticalStackLayout>
    </Border>
</ContentView>

步骤3:简化后台代码

删除原来的Progress属性,后台代码仅保留两个可绑定属性即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 14:42:02