MVVM模式中,BaseViewModel 的 IsBusy 属性的作用

MVVM模式中BaseViewModel的IsBusy属性解析

MVVM模式中,BaseViewModel 的 IsBusy 属性的作用

在MVVM模式中,BaseViewModelIsBusy 属性主要用于管理ViewModel的异步操作状态,确保界面与后台任务的协调。以下是其核心用途和实现细节:


1. 核心作用

  • 防止重复操作
    当异步任务(如网络请求、数据库查询)执行时,将 IsBusy 设为 true,禁用按钮或控件,避免用户重复触发。

  • UI状态反馈
    在界面显示加载动画(如旋转图标、进度条),提示用户操作正在进行中。

  • 统一状态管理
    作为基类属性,所有继承的ViewModel均可复用,减少重复代码。


2. 典型使用场景

// 在 BaseViewModel 中定义
public class BaseViewModel : INotifyPropertyChanged 
{
    private bool _isBusy;
    public bool IsBusy 
    {
        get => _isBusy;
        set 
        {
            _isBusy = value;
            OnPropertyChanged();
            // 触发命令可执行性更新(如按钮禁用)
            OnPropertyChanged(nameof(IsNotBusy)); 
        }
    }
    
    // 方便绑定反向逻辑(如按钮的 IsEnabled)
    public bool IsNotBusy => !IsBusy;

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string name = null) 
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
  • 绑定到界面控件

    <Button Text="加载数据" 
            Command="{Binding LoadDataCommand}" 
            IsEnabled="{Binding IsNotBusy}"/>
    <ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="True"/>
    
  • 异步操作中的状态管理

    public class UserViewModel : BaseViewModel 
    {
        public ICommand LoadDataCommand => new Command(async () => 
        {
            if (IsBusy) return; // 防止重复执行
    
            IsBusy = true;
            try 
            {
                await LoadDataAsync(); // 异步任务
            }
            finally 
            {
                IsBusy = false; // 确保状态重置
            }
        });
    }
    

3. 高级优化

  • 计数器模式
    处理多个并发任务时,使用计数器代替布尔值:

    private int _busyCounter;
    public bool IsBusy 
    {
        get => _busyCounter > 0;
        set 
        {
            _busyCounter = Math.Max(0, value ? _busyCounter + 1 : _busyCounter - 1);
            OnPropertyChanged();
        }
    }
    
  • 附加状态信息
    扩展 BusyMessage 属性,提供更详细的提示:

    private string _busyMessage;
    public string BusyMessage 
    {
        get => _busyMessage;
        set 
        {
            _busyMessage = value;
            OnPropertyChanged();
        }
    }
    
    // 使用时:
    IsBusy = true;
    BusyMessage = "正在加载用户数据...";
    

4. 注意事项

  • 线程安全
    异步操作可能在其他线程修改 IsBusy,需确保通过 DispatcherMainThread.BeginInvokeOnMainThread(Xamarin)更新UI属性。

  • 异常处理
    try/catch/finally 中确保 IsBusy 被正确重置,避免任务异常后界面“卡死”。


通过 IsBusy,MVVM模式实现了业务逻辑与UI状态的解耦,提升代码可维护性,同时增强用户体验。

' 090_BaseViewModel.vb Imports System.ComponentModel Imports System.Runtime.CompilerServices Imports System.Collections.Generic Imports PrnComexcep Imports PrnUtlthods Public MustInherit Class BaseViewModel Implements INotifyPropertyChanged Implements IDataErrorInfo Private ReadOnly _errors As New Dictionary(Of String, String)() Private _isBusy As Boolean = False Private _isLoading As Boolean = False Private _lastOperationMessage As String = String.Empty Private _lastOperationSuccess As Boolean = False Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged Public Property IsBusy As Boolean Get Return _isBusy End Get Set(value As Boolean) If SetProperty(_isBusy, value, Function() IsBusy) Then OnPropertyChanged(NameOf(IsLoading)) End If End Set End Property Public Property IsLoading As Boolean Get Return _isLoading End Get Set(value As Boolean) If SetProperty(_isLoading, value, Function() IsLoading) Then OnPropertyChanged(NameOf(IsBusy)) End If End Set End Property Public Property LastOperationMessage As String Get Return _lastOperationMessage End Get Set(value As String) SetProperty(_lastOperationMessage, value, Function() LastOperationMessage) End Set End Property Public Property LastOperationSuccess As Boolean Get Return _lastOperationSuccess End Get Set(value As Boolean) SetProperty(_lastOperationSuccess, value, Function() LastOperationSuccess) End Set End Property Protected Function SetProperty(Of T)(ByRef storage As T, value As T, <CallerMemberName> Optional propertyName As String = Nothing) As Boolean If EqualityComparer(Of T).Default.Equals(storage, value) Then Return False storage = value OnPropertyChanged(propertyName) ValidateProperty(propertyName, value) Return True End Function Protected Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub Protected Sub OnPropertyChanged(ParamArray propertyNames As String()) For Each propertyName In propertyNames OnPropertyChanged(propertyName) Next End Sub #Region "Validation Support" Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error Get Return If(_errors.Values.FirstOrDefault(), String.Empty) End Get End Property Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item Get If _errors.ContainsKey(columnName) Then Return _errors(columnName) End If Return String.Empty End Get End Property Public ReadOnly Property HasErrors As Boolean Get Return _errors.Count > 0 End Get End Property Public ReadOnly Property Errors As IReadOnlyDictionary(Of String, String) Get Return _errors End Get End Property Protected Overridable Sub ValidateProperty(propertyName As String, value As Object) ClearError(propertyName) Dim errorMessage As String = ValidatePropertyInternal(propertyName, value) If Not String.IsNullOrEmpty(errorMessage) Then AddError(propertyName, errorMessage) End If End Sub Protected Overridable Function ValidatePropertyInternal(propertyName As String, value As Object) As String ' 子类可以重写此方法实现具体的验证逻辑 Return String.Empty End Function Protected Sub AddError(propertyName As String, errorMessage As String) _errors(propertyName) = errorMessage OnPropertyChanged(NameOf(Errors)) OnPropertyChanged(NameOf(HasErrors)) End Sub Protected Sub ClearError(propertyName As String) If _errors.ContainsKey(propertyName) Then _errors.Remove(propertyName) OnPropertyChanged(NameOf(Errors)) OnPropertyChanged(NameOf(HasErrors)) End If End Sub Protected Sub ClearAllErrors() _errors.Clear() OnPropertyChanged(NameOf(Errors)) OnPropertyChanged(NameOf(HasErrors)) End Sub #End Region #Region "Command Execution Support" Protected Async Function ExecuteAsync(operation As Func(Of Task), Optional operationName As String = "") As Task(Of Boolean) If IsBusy Then Return False IsBusy = True LastOperationMessage = String.Empty LastOperationSuccess = False Try Await operation() LastOperationSuccess = True LastOperationMessage = If(String.IsNullOrEmpty(operationName), "操作完成", $"{operationName}完成") Return True Catch ex As BusinessException LastOperationMessage = ex.Message Logger.Warn($"业务操作失败: {operationName}", ex) Return False Catch ex As DatabaseException LastOperationMessage = "数据库操作失败,请稍后重试" Logger.Error($"数据库操作失败: {operationName}", ex) Return False Catch ex As NetworkException LastOperationMessage = "网络连接失败,请检查网络设置" Logger.Error($"网络操作失败: {operationName}", ex) Return False Catch ex As Exception LastOperationMessage = "系统错误,请联系管理员" Logger.Error($"系统操作失败: {operationName}", ex) Return False Finally IsBusy = False End Try End Function Protected Async Function ExecuteAsync(Of T)(operation As Func(Of Task(Of T)), Optional operationName As String = "") As Task(Of T) If IsBusy Then Return Nothing IsBusy = True LastOperationMessage = String.Empty LastOperationSuccess = False Try Dim result = Await operation() LastOperationSuccess = True LastOperationMessage = If(String.IsNullOrEmpty(operationName), "操作完成", $"{operationName}完成") Return result Catch ex As BusinessException LastOperationMessage = ex.Message Logger.Warn($"业务操作失败: {operationName}", ex) Return Nothing Catch ex As DatabaseException LastOperationMessage = "数据库操作失败,请稍后重试" Logger.Error($"数据库操作失败: {operationName}", ex) Return Nothing Catch ex As NetworkException LastOperationMessage = "网络连接失败,请检查网络设置" Logger.Error($"网络操作失败: {operationName}", ex) Return Nothing Catch ex As Exception LastOperationMessage = "系统错误,请联系管理员" Logger.Error($"系统操作失败: {operationName}", ex) Return Nothing Finally IsBusy = False End Try End Function Protected Sub ExecuteSync(operation As Action, Optional operationName As String = "") If IsBusy Then Return IsBusy = True LastOperationMessage = String.Empty LastOperationSuccess = False Try operation() LastOperationSuccess = True LastOperationMessage = If(String.IsNullOrEmpty(operationName), "操作完成", $"{operationName}完成") Catch ex As BusinessException LastOperationMessage = ex.Message Logger.Warn($"业务操作失败: {operationName}", ex) Catch ex As DatabaseException LastOperationMessage = "数据库操作失败,请稍后重试" Logger.Error($"数据库操作失败: {operationName}", ex) Catch ex As NetworkException LastOperationMessage = "网络连接失败,请检查网络设置" Logger.Error($"网络操作失败: {operationName}", ex) Catch ex As Exception LastOperationMessage = "系统错误,请联系管理员" Logger.Error($"系统操作失败: {operationName}", ex) Finally IsBusy = False End Try End Sub #End Region #Region "Data Loading Support" Protected Async Function LoadDataAsync(Of T)(dataLoader As Func(Of Task(Of T)), Optional loadingMessage As String = "加载中...") As Task(Of T) If IsLoading Then Return Nothing IsLoading = True LastOperationMessage = loadingMessage Try Dim result = Await dataLoader() LastOperationSuccess = True LastOperationMessage = "加载完成" Return result Catch ex As Exception LastOperationSuccess = False LastOperationMessage = "数据加载失败" Logger.Error($"数据加载失败", ex) Return Nothing Finally IsLoading = False End Try End Function Protected Sub LoadDataSync(Of T)(dataLoader As Func(Of T), Optional loadingMessage As String = "加载中...") If IsLoading Then Return IsLoading = True LastOperationMessage = loadingMessage Try Dim result = dataLoader() LastOperationSuccess = True LastOperationMessage = "加载完成" Catch ex As Exception LastOperationSuccess = False LastOperationMessage = "数据加载失败" Logger.Error($"数据加载失败", ex) Finally IsLoading = False End Try End Sub #End Region #Region "Dispose Pattern" Private _disposedValue As Boolean = False Protected Overridable Sub Dispose(disposing As Boolean) If Not _disposedValue Then If disposing Then ' 释放托管资源 ClearAllErrors() End If _disposedValue = True End If End Sub Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub #End Region End Class
最新发布
11-14
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值