MVVM模式主要目的是将UI(View)与数据和业务逻辑分开(Model)。这种分离是通过视图模型(ViewModel)实现的。该类充当视图和模型之间的中介。
MVVM模式几个优势
-
可重复使用,业务逻辑可以在不同的模块之间重用。
-
可测试,在MVVM模式种,可以轻松的隔离和测试视图、模型和视图模型代码。
-
更容易理解的代码结构,UI代码在视图中,业务逻辑和数据结构位于模型中,视图模型充当它们的连接器,项目会变得更容易理解。
一个简单视图模型案例
-
创建一个项目添加MyViewModel,声明一个TextValue用来存储一个按钮中的文本并对其修改。
-
添加一个按钮命令来显示按钮文本中显示的点击次数。
public class MyViewModel:INotifyPropertyChanged
{
private int count = 0;
private string textValue = "按钮";
public string TextValue
{
get => textValue;
set
{
textValue = value;
OnPropertyChanged();
}
}
public ICommand UpdateTextCommand { get; set; }
public MyViewModel()
{
UpdateTextCommand = new Command(UpdateText);
}
public void UpdateText()
{
count++;
if (count == 1)
TextValue = $"点击了{count}次";
else
TextValue = $"点击了{count}次";
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
-
删除MainPage.cs代码中的count变量和按钮事件
-
在MainPage.xaml中使用BindingContext添加对MyViewModel的引用并修改button中的代码
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MauiApp1"
x:Class="MauiApp1.MainPage">
<ContentPage.BindingContext>
<vm:MyViewModel/>
</ContentPage.BindingContext>
<ScrollView>
<VerticalStackLayout
Padding="30,0"
Spacing="25">
<Image
Source="dotnet_bot.png"
HeightRequest="185"
Aspect="AspectFit"
SemanticProperties.Description="dot net bot in a race car number eight" />
<Label
Text="Hello, World!"
Style="{StaticResource Headline}"
SemanticProperties.HeadingLevel="Level1" />
<Label
Text="Welcome to .NET Multi-platform App UI"
Style="{StaticResource SubHeadline}"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I" />
<Button
x:Name="CounterBtn"
Text="{Binding TextValue}"
SemanticProperties.Hint="Counts the number of times you click"
Command="{Binding UpdateTextCommand}"
HorizontalOptions="Fill" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
CommunityToolkit.Mvvm
-
在MVVM模式中通常涉及到INotifyPropertyChanged接口的支持和命令属性会编写大量的样板代码,会让代码变得很繁琐容易导致错误。
-
在.NET社区提供了CommunityToolkit.Mvvm代码生成器,会让我们在实现MVVM是方便很多。
-
我们在项目中添加CommunityToolkit.Mvvm工具包,并修改MyViewModel中的代码
public partial class MyViewModel:ObservableObject
{
private int count = 0;
[ObservableProperty]
private string textValue = "按钮";
[RelayCommand]
private void UpdateText()
{
count++;
TextValue = count==1 ? $"点击了{count}次" : $"点击了{count}次";
}
}
-
我们可以对比一下前后在MyViewModel中的代码量
-
使用CommunityToolkit.Mvvm代码生成器简洁了很多。
-
这里要注意需要在类中添加partial关键字。
-
还有一点如果该类继承了其他类,可以是使用[INotifyPropertyChanged]特性来标记该类同样可以实现ObservableObject的功能
[INotifyPropertyChanged]
public partial class MyViewModel : SomeOtherBase
{
......
}
-
当一个属性依赖于另一个属性时可以使用[NotifyPropertyChangedFor(nameof(被通知属性))]来通知当前属性已更改。
-
在[RelayCommand]中可以使用CanExecute来控制该命令是否可执行。
下面是使用CommunityToolkit.Mvvm工具包的一个案例
-
我们在视图模型中使用ObservableCollection创建一个用户集合并绑定到CollectionView中.
-
[RelayCommand]特性会对Initialize生成一个InitializeCommand。
-
[NotifyCanExecuteChangedFor(nameof(InitializeCommand))]指示当IsInitialized属性更改时,通知InitializeCommand的CanExecute 方法重新检查其可执行性。
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace MauiApp1;
public partial class MyViewModel : ObservableObject
{
[ObservableProperty] private ObservableCollection<User> users;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(InitializeCommand))]
private bool isInitialized;
[RelayCommand(CanExecute = nameof(CanInitialize))]
private async Task InitializeAsync()
{
Users = new ObservableCollection<User>(await DummyService.GetUsersAsync());
IsInitialized = true;
}
public bool CanInitialize() => !IsInitialized;
}
/// <summary>
/// 模拟远程获取用户数据服务,模拟耗时5秒操作
/// </summary>
public static class DummyService
{
public static async Task<IEnumerable<User>> GetUsersAsync()
{
await Task.Delay(5000);
return new List<User>()
{
new User
{
Id = 1,
Name = "张飞",
},
new User
{
Id = 2,
Name = "刘备",
},
new User
{
Id = 3,
Name = "赵云",
},
new User
{
Id = 4,
Name = "关羽",
},
};
}
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
引入CommunityToolkit.Maui工具包
-
是一组用于.NET MAUI开发的动画、行为、转换器和自定义视图的工具包。
-
安装后,需要在MauiProgram.cs文件中对它进行引用。
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
-
来到主页我们使用该工具包中的EventToCommandBehavior来绑定Viewmodel中的初始化命令。
-
注意需要对toolkit命名空间添加引用。
-
定义CollectionView并绑定到user,添加ActivityIndicator模拟数据加载。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MauiApp1"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="MauiApp1.MainPage">
<ContentPage.Behaviors>
<toolkit:EventToCommandBehavior
EventName="Loaded"
Command="{Binding InitializeCommand}"/>
</ContentPage.Behaviors>
<ContentPage.BindingContext>
<vm:MyViewModel/>
</ContentPage.BindingContext>
<Grid>
<CollectionView ItemsSource="{Binding Users}">
<CollectionView.ItemTemplate>
<DataTemplate
x:DataType="vm:User">
<HorizontalStackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Id}"/>
</HorizontalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ActivityIndicator IsRunning="{Binding InitializeCommand.IsRunning}"/>
</Grid>
</ContentPage>
运行程序
-
启动项目模拟加载5秒,然后显示集合.

1万+

被折叠的 条评论
为什么被折叠?



