MVVM 和 依赖注入 有助于开发出松耦合可维护以及可测试的应用程序。
依赖注入在MAUI中是原生支持的。
创建一个MAUI APP项目,根目录会有MauiProgram.cs
public static MauiApp CreateMauiApp()
{var builder = MauiApp.CreateBuilder();builder.UseMauiApp().ConfigureFonts(fonts =>{fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");});// 注入页面builder.Services.AddTransient();return builder.Build();
}
BaseViewModel会被后来的viewmodel所继承,并且所有的公共属性、方法都将再次进行声明。
BaseViewModel需要继承和实现INotifyPropertyChanged才能够实现view和viewmodel之间的通讯。
using System.ComponentModel;
using System.Runtime.CompilerServices;namespace CrossPlatformTools.ViewModel
{public class BaseViewModel : INotifyPropertyChanged{private bool isBusy;public bool IsBusy{get => isBusy;set{if (isBusy == value)return;isBusy = value;OnPropertyChanged();OnPropertyChanged(nameof(IsNoteBusy));}}public bool IsNoteBusy => !IsBusy;private string title;public string Title{get => title;set{if (title == value)return;title = value;OnPropertyChanged();}}public event PropertyChangedEventHandler PropertyChanged;public void OnPropertyChanged([CallerMemberName] string name = null) =>PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));}
}
MainViewModel继承BaseViewModel但是MainViewModel中独有的属性和方法还是需要的MainViewModel中进行声明的。
namespace CrossPlatformTools.ViewModel
{public class MainViewModel : BaseViewModel{private int count = 0;public int Count{get { return count; }set{if (count == value)return;count = value;OnPropertyChanged();}}public Command CountCommand { get; }public MainViewModel(){Title = "首页";CountCommand = new Command(OnCounter);}private void OnCounter(){Count++;}}
}
在 BaseViewModel 声明的属性 MainViewModel 中就可以直接使用了。
Command的声明相比于通知属性就简便很多。声明Command然后在构造函数中调用即可。
在 xaml 中需要引入命名空间:
xmlns:viewmodel="clr-namespace:CrossPlatformTools.ViewModel"
只引入命名空间这时想要使用属性是无法提示的,还需要设置DataType:
x:DataType="viewmodel:MainViewModel"
你以为这样就完了吗,当然不是,只在xaml中引入命名空间进行
Command和Property的Binding 是不行了,还要进行上下文绑定。
using CrossPlatformTools.ViewModel;namespace CrossPlatformTools.View;public partial class MainPage : ContentPage
{public MainPage(MainViewModel mainViewModel){InitializeComponent();BindingContext = mainViewModel;}
}
这里有的一不同点,一般来说
BindingContext是这样做的BindingContext = new MainViewModel()但是我这里使用的是构造函数注入。
不能直接使用 需要在MauiProgram.cs中注入才能使用。
原生的方式实现 MVVM 时,
ViewModel中的属性定义非常需要很多的代码,并且要手动实现PropertyChanged事件。
CommunityToolkit.Mvvm 实现官方描述:
此包包含一个.NET MVVM库,其中包含以下帮助程序:
- ObservableObject:实现INotifyPropertyChanged接口的对象的基类。
- ObservableRecipient:支持IMessenger服务的可观察对象的基类。
- ObservableValidator:实现INotifyDataErrorInfo接口的对象的基类。
- RelayCommand:一个实现ICommand接口的简单委托命令。
- AsyncRelayCommand:支持异步操作和取消的委托命令。
- WeakReferenceMessenger:通过不同的松散耦合对象交换消息的消息传递系统。
- StrongReferenceMessenger:一个高性能的消息传递系统,以弱引用换取速度。
- Ioc:用于配置依赖注入服务容器的助手类。
需要引入
CommunityToolkit.Mvvm.ComponentModel命名空间
使用partialclass 标识符,CommunityToolkit.Mvvm内部实现使用的是源生成器,所有需要使用partial进行标识。
变量的声明必须使用驼峰式命名规则,才可以与生成的以 Pascal 命名规则的属性名不重复。
关于 源生成器 不明白的可以去看前面的文章 C# 源代码生成器 讲解了 源生成器的用法。
基类就变成了下面这样,少了很多的代码
using CommunityToolkit.Mvvm.ComponentModel;namespace CrossPlatformTools.ViewModel
{public partial class BaseViewModel : ObservableObject{[ObservableProperty][NotifyPropertyChangedFor(nameof(isNoteBusy))]bool isBusy;bool isNoteBusy => !isBusy;[ObservableProperty]string title;}
}
using CommunityToolkit.Mvvm.ComponentModel;namespace CrossPlatformTools.ViewModel
{public partial class MainViewModel : BaseViewModel{[ObservableProperty]private int count = 0;public Command CountCommand { get; }public MainViewModel(){Title = "首页";CountCommand = new Command(OnCounter);}private void OnCounter(){Count++;}}
}
