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

如何在MAUI所有页面复用带OnPropertyChanged的IsLoading属性?

MAUI全局复用IsLoading状态管理的几种方案

你要在所有页面实现ActivityIndicator绑定IsLoading的功能,核心就是把重复的INotifyPropertyChanged实现和IsLoading属性抽出来复用,下面是几种实用的做法:

1. 用MVVM基类ViewModel(推荐)

如果你的项目采用MVVM模式,直接把重复代码做成基类ViewModel,所有页面的ViewModel都继承它:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace YourAppName.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private bool _isLoading;
        public bool IsLoading
        {
            get => _isLoading;
            set
            {
                _isLoading = value;
                OnPropertyChanged();
            }
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

页面的ViewModel直接继承后,就能直接使用IsLoading属性:

public class HomeViewModel : BaseViewModel
{
    public async Task FetchDataFromApi()
    {
        IsLoading = true;
        try
        {
            // 这里编写API调用逻辑
            // await ApiService.GetData();
        }
        finally
        {
            // 无论请求成功失败,都要关闭加载状态
            IsLoading = false;
        }
    }
}

页面XAML中绑定ActivityIndicator:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:YourAppName.ViewModels"
             x:Class="YourAppName.Views.HomePage"
             BindingContext="{StaticResource HomeViewModel}">

    <!-- 页面原有内容 -->

    <ActivityIndicator IsRunning="{Binding IsLoading}"
                       IsVisible="{Binding IsLoading}"
                       HorizontalOptions="Center"
                       VerticalOptions="Center"
                       HeightRequest="40" WidthRequest="40" />

</ContentPage>

2. 不使用MVVM?做页面基类

如果项目没采用MVVM模式,就给页面创建一个基类,所有页面继承这个基类:

using Microsoft.Maui.Controls;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace YourAppName.Views
{
    public class BasePage : ContentPage, INotifyPropertyChanged
    {
        public new event PropertyChangedEventHandler PropertyChanged;

        private bool _isLoading;
        public bool IsLoading
        {
            get => _isLoading;
            set
            {
                _isLoading = value;
                OnPropertyChanged();
            }
        }

        protected new void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

页面代码继承BasePage,并将绑定上下文设为自身:

public partial class HomePage : BasePage
{
    public HomePage()
    {
        InitializeComponent();
        BindingContext = this;
    }

    private async Task CallApi()
    {
        IsLoading = true;
        try
        {
            // API调用逻辑
        }
        finally
        {
            IsLoading = false;
        }
    }
}

对应的XAML也要修改基类引用:

<local:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                xmlns:local="clr-namespace:YourAppName.Views"
                x:Class="YourAppName.Views.HomePage">

    <ActivityIndicator IsRunning="{Binding IsLoading}"
                       IsVisible="{Binding IsLoading}"
                       HorizontalOptions="Center"
                       VerticalOptions="Center" />

</local:BasePage>

3. 进阶:封装自定义加载控件

如果想避免每个页面重复写ActivityIndicator的XAML,可以封装一个包含遮罩层的自定义加载控件:

using Microsoft.Maui.Controls;

namespace YourAppName.Controls
{
    public class LoadingOverlay : ContentView
    {
        // 定义可绑定属性,方便外部绑定IsLoading状态
        public static readonly BindableProperty IsLoadingProperty =
            BindableProperty.Create(nameof(IsLoading), typeof(bool), typeof(LoadingOverlay), false);

        public bool IsLoading
        {
            get => (bool)GetValue(IsLoadingProperty);
            set => SetValue(IsLoadingProperty, value);
        }

        public LoadingOverlay()
        {
            var indicator = new ActivityIndicator
            {
                HorizontalOptions = LayoutOptions.Center,
                VerticalOptions = LayoutOptions.Center,
                HeightRequest = 40,
                WidthRequest = 40
            };
            // 将指示器的运行状态和可见性绑定到IsLoading
            indicator.SetBinding(ActivityIndicator.IsRunningProperty, nameof(IsLoading));
            indicator.SetBinding(ActivityIndicator.IsVisibleProperty, nameof(IsLoading));

            // 半透明遮罩层,覆盖整个页面
            var overlay = new AbsoluteLayout
            {
                BackgroundColor = Color.FromRgba(0, 0, 0, 0.5),
                IsVisible = false
            };
            overlay.SetBinding(AbsoluteLayout.IsVisibleProperty, nameof(IsLoading));
            
            // 将指示器定位在遮罩中央
            AbsoluteLayout.SetLayoutFlags(indicator, AbsoluteLayoutFlags.PositionProportional);
            AbsoluteLayout.SetLayoutBounds(indicator, new Rect(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
            overlay.Children.Add(indicator);

            Content = overlay;
        }
    }
}

之后在页面中只需一行代码就能添加加载状态:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:YourAppName.Controls"
             x:Class="YourAppName.Views.HomePage">

    <!-- 页面原有内容 -->

    <!-- 加载遮罩,自动铺满整个页面 -->
    <controls:LoadingOverlay IsLoading="{Binding IsLoading}"
                             AbsoluteLayout.LayoutFlags="All"
                             AbsoluteLayout.LayoutBounds="0,0,1,1" />

</ContentPage>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:40:36