很开心,终于到了创建页面的时候了!
我们需要两个页面
新建一个MainPageViewModel.cs,作为MainPage的ViewModel层
public class MainPageViewModel : ViewModelBase
{
private readonly IRepository songRepository;
public MainPageViewModel(IRepository songRepository)
{
this.RefreshCommand=new Command(Refresh, (o) => true);
this.DeleteCommand=new Command(Delete, (o) => true);
this.songRepository=songRepository;
}
private void Delete(object obj)
{
songRepository.Delete(obj as Song);
}
private async void Refresh(object obj)
{
this.IsRefreshing=true;
var getSongs = this.songRepository.GetAllListAsync();
await getSongs.ContinueWith(r => IsRefreshing=false);
var songs = await getSongs;
this.Songs=new ObservableCollection(songs);
}
private ObservableCollection songs;
public ObservableCollection Songs
{
get { return songs; }
set
{
songs = value;
RaisePropertyChanged();
}
}
private Song currentSong;
public Song CurrentSong
{
get { return currentSong; }
set
{
currentSong = value;
RaisePropertyChanged();
}
}
private bool _isRefreshing;
public bool IsRefreshing
{
get { return _isRefreshing; }
set
{
_isRefreshing = value;
RaisePropertyChanged();
}
}
public Command RefreshCommand { get; set; }
public Command DeleteCommand { get; private set; }
}
新建一个MainPage页面
编写Xaml为:
注意这个页面将继承MauiBoilerplate.ContentPageBase
编写CodeBehind为:
注意将它继承ITransientDependency接口
这个页面之前提到过,已经通过IocManager.Resolve(typeof(MainPage))解析出实例并赋值给App.MainPage了。
public partial class MainPage : ContentPageBase, ITransientDependency
{
private readonly MainPageViewModel mainPageViewModel;
private readonly MusicItemPageViewModel musicItemPageViewModel;
private readonly MusicItemPage musicItemPage;
public MainPage(MainPageViewModel mainPageViewModel, MusicItemPageViewModel musicItemPageViewModel, MusicItemPage musicItemPage)
{
InitializeComponent();
this.mainPageViewModel=mainPageViewModel;
this.musicItemPageViewModel=musicItemPageViewModel;
this.musicItemPage=musicItemPage;
BindingContext=this.mainPageViewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
mainPageViewModel.RefreshCommand.Execute(null);
}
private async void SongMoreButton_OnClicked(object sender, EventArgs e)
{
var currentsong = (sender as BindableObject).BindingContext as Song;
string action = await DisplayActionSheet(currentsong.MusicTitle, "取消", null, "修改", "删除");
if (action=="修改")
{
musicItemPageViewModel.CurrentSong = currentsong;
await Navigation.PushModalAsync(musicItemPage);
}
else if (action=="删除")
{
mainPageViewModel.DeleteCommand.Execute(currentsong);
mainPageViewModel.RefreshCommand.Execute(null);
}
}
private async void AddButton_Clicked(object sender, EventArgs e)
{
musicItemPageViewModel.CurrentSong = new Song();
await Navigation.PushModalAsync(musicItemPage);
}
}
此页面将显示一个列表,并在列表条目下可以弹出一个菜单
新建一个MusicItemPageViewModel.cs,作为MusicItemPage的ViewModel层
public class MusicItemPageViewModel : ViewModelBase
{
private readonly IIocResolver iocResolver;
private readonly IRepository songRepository;
public event EventHandler OnFinished;
public MusicItemPageViewModel(
IIocResolver iocResolver,
IRepository songRepository)
{
this.CommitCommand=new Command(Commit, (o) => CurrentSong!=null);
this.iocResolver=iocResolver;
this.songRepository=songRepository;
this.PropertyChanged+=MusicItemPageViewModel_PropertyChanged;
}
private void MusicItemPageViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName==nameof(CurrentSong))
{
CommitCommand.ChangeCanExecute();
}
}
private void Commit(object obj)
{
songRepository.InsertOrUpdate(currentSong);
}
private Song currentSong;
public Song CurrentSong
{
get { return currentSong; }
set
{
currentSong = value;
RaisePropertyChanged();
}
}
}
新建一个MusicItemPage 页面
编写Xaml为:
注意这个页面将继承MauiBoilerplate.ContentPageBase
编写CodeBehind为:
注意将它继承ITransientDependency接口
public partial class MusicItemPage : ContentPageBase, ITransientDependency
{
private readonly MusicItemPageViewModel musicItemPageViewModel;
public MusicItemPage(MusicItemPageViewModel musicItemPageViewModel)
{
InitializeComponent();
this.musicItemPageViewModel=musicItemPageViewModel;
this.musicItemPageViewModel.OnValidateErrors+=MusicItemPageViewModel_OnValidateErrors;
this.musicItemPageViewModel.OnFinished+=MusicItemPageViewModel_OnFinished;
BindingContext=this.musicItemPageViewModel;
Unloaded+=MusicItemPage_Unloaded;
}
private async void MusicItemPageViewModel_OnFinished(object sender, EventArgs e)
{
await this.Navigation.PopModalAsync();
}
private void MusicItemPage_Unloaded(object sender, EventArgs e)
{
musicItemPageViewModel.CurrentSong = null;
}
private async void MusicItemPageViewModel_OnValidateErrors(object sender, List e)
{
var content = string.Join(',', e);
await DisplayAlert("请注意", content, "好的");
}
}
这个页面提供歌曲条目新增和编辑的交互功能
这个部分使用Abp的ValidationConfiguration功能校验表单数据,以展示Abp功能的使用
首先在MusicItemPageViewModel 构造函数中添加对IValidationConfiguration对象的注入
添加OnValidateErrors事件,并且在Page中订阅这个事件。此事件将在校验未通过时触发
MusicItemPageViewModel.cs中:
public event EventHandler> OnValidateErrors;
MusicItemPage.xaml.cs中:
this.musicItemPageViewModel.OnValidateErrors+=MusicItemPageViewModel_OnValidateErrors;
private async void MusicItemPageViewModel_OnValidateErrors(object sender, List e)
{
var content = string.Join(',', e);
await DisplayAlert("请注意", content, "好的");
}
编写校验逻辑代码
MusicItemPageViewModel.cs中:
protected List GetValidationErrors(Song validatingObject)
{
List validationErrors = new List();
foreach (var validatorType in _configuration.Validators)
{
using (var validator = iocResolver.ResolveAsDisposable(validatorType))
{
var validationResults = validator.Object.Validate(validatingObject);
validationErrors.AddRange(validationResults);
}
}
return validationErrors;
}
Commit提交方法,改造如下:
当GetValidationErrors返回的校验错误列表中有内容时,将OnValidateErrors事件Invoke
private void Commit(object obj)
{
var validateErrors = GetValidationErrors(this.CurrentSong);
if (validateErrors.Count==0)
{
songRepository.InsertOrUpdate(currentSong);
this.OnFinished?.Invoke(this, EventArgs.Empty);
}
else
{
OnValidateErrors?.Invoke(this, validateErrors);
}
}
接下来在实体中定义校验规则,校验器将按照这些规则返回校验结果
public class Song : FullAuditedEntity, IValidatableObject
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
[Required]
[StringLength(6, ErrorMessage = "歌曲名称要在6个字以内")]
public string MusicTitle { get; set; }
[Required]
[StringLength(10, ErrorMessage = "歌曲名称要在10个字以内")]
public string Artist { get; set; }
[Required]
[StringLength(10, ErrorMessage = "歌曲名称要在10个字以内")]
public string Album { get; set; }
public TimeSpan Duration { get; set; }
public DateTime ReleaseDate { get; set; }
public IEnumerable Validate(ValidationContext validationContext)
{
if (ReleaseDate != default && ReleaseDate>DateTime.Now)
{
yield return new ValidationResult("ReleaseDate不能大于当天",
new[] { nameof(ReleaseDate) });
}
}
}
运行,新建条目。当我们如下填写的时候,将会弹出提示框
iOS平台也测试通过
至此我们完成了所有的工作。
Abp是一个很好用的.Net开发框架,Abp库帮助我们抽象了整个项目以及更多的设计模式应用,其名称Asp Boilerplate,虽然有一个Asp在其中,但其功能不仅仅可以构建AspNet Core应用,
经过我们的探索用Abp构建了跨平台应用,同样它还可以用于Xamarin,Wpf甚至是WinForms这些基于桌面的应用。
欢迎参与讨论和转发。
jevonsflash/maui-abp-sample (github.com)