Maui学习笔记- SQLite简单使用案例02添加详情页

  • 我们继续上一个案例,实现一个可以修改当前用户信息功能。

    当用户点击某个信息时,跳转到信息详情页,然后可以点击编辑按钮导航到编辑页面。

创建项目 

  • 我们首先在ViewModels目录下创建UserDetailViewModel。

  • 实现从详情信息页面导航到编辑页面。

  • 这里要使用一个字典来传输对象。

public partial class UserDetailViewModel:ObservableObject,IQueryAttributable
{
    [ObservableProperty] private User itemUser;

    public Func ParentRefreshAction { get;set; }

    [RelayCommand]
    async Task ShowEditFormAsync()
    {
        var context = new UserContext();
        var editedItem = context.Users.FirstOrDefault(u => u.Id == ItemUser.Id);

        await Shell.Current.GoToAsync(nameof(UserEditPage),
            parameters: new Dictionary
            {
                { "ParentRefreshAction", (Func)ItemEditedAsync },
                { "Item", editedItem }
            });
    }
    
    async Task ItemEditedAsync(User user) {
        ItemUser = user;
        await ParentRefreshAction(user);
    }

    public virtual void ApplyQueryAttributes(IDictionary query)
    {
        if (query.TryGetValue("Item",out object currentItem))
        {
            ItemUser = (User)currentItem;
        }

        if (query.TryGetValue("ParentRefreshAction",out object parentRefreshAction))
        {
            ParentRefreshAction = (Func)parentRefreshAction;
        }
        query.Clear();
    }
}
  • 创建用户详情页面,用来显示用户的全部信息。

  • 在ToolbarItem中添加一个命令导航到编辑页面。


    
        
    
    
        
    
    
        
  • 更新UserEditViewModel,我们让他直接继承自UserDetailViewModel.

public partial class UserEditViewModel:UserDetailViewModel
{
    [ObservableProperty] private bool isNewItem;
    

    [RelayCommand]
    private async Task SaveAsync()
    {
        await using var context = new UserContext();


        if (IsNewItem)
        {
            context.Users.Add(ItemUser);
        }
        else
        {
            context.Users.Attach(ItemUser);
            context.Entry(ItemUser).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
        }

        context.SaveChangesAsync();
        
        await ParentRefreshAction(ItemUser);
        
        await Shell.Current.GoToAsync("..");
    }
    
    public override void ApplyQueryAttributes(IDictionary query) {
        if (query.TryGetValue("IsNewItem", out object isNew)) {
             IsNewItem = (bool)isNew;
        }
        base.ApplyQueryAttributes(query);
    }
}

 

  • 修改MainViewModel。

public partial class MainViewModel:ObservableObject
{
    [ObservableProperty]
    ObservableCollection users;

    [ObservableProperty] private bool refreshing;

    [RelayCommand]
    private async Task LoadUsersAsync()
    {
        await Task.Run(() =>
        {
            using var context = new UserContext();

            Users = new ObservableCollection(context.Users);
        });

        Refreshing = false;
    }

    [RelayCommand]
    private void Showing()
    {
        Refreshing = true;
    }

    [RelayCommand]
    private void DeleteUser(User user)
    {
        var context = new UserContext();

        context.Users.Remove(user);
        
        context.SaveChanges();
        
        Users.Remove(user);
    }

    [RelayCommand]
    private async Task ShowNewFormAsync()
    {
        await Shell.Current.GoToAsync(nameof(UserEditPage),parameters:new Dictionary
        {
            {"ParentRefreshAction",(Func)RefreshAddedAsync},
            {"Item",new User()},
            {"IsNewItem",true}
        });
    }

    Task RefreshAddedAsync(User addedUser)
    {
        Users.Add(addedUser);
        return Task.CompletedTask;
    }

    [RelayCommand]
    async Task ShowDetailFormAsync(User user)
    {
        await Shell.Current.GoToAsync(nameof(UserDetailPage),parameters:new Dictionary
        {
            {"ParentRefreshAction",(Func)RefreshEditedAsync},
            {"Item",user},
        });
    }

    async Task RefreshEditedAsync(User updataUser)
    {
        int editedItemIndex = -1;
        await Task.Run(() =>
        {
            editedItemIndex = Users.Select(
                    (user, index) => new { user, index })
                .First(x => x.user.Id == updataUser.Id)
                .index;
        });

        if (editedItemIndex==-1)
        {
            return;
        }
        Users[editedItemIndex] = updataUser;
    }
}
  • 注册路由

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        Routing.RegisterRoute(nameof(UserEditPage),typeof(UserEditPage));
        Routing.RegisterRoute(nameof(UserDetailPage),typeof(UserDetailPage));
    }
}

 

  • 在主页添加GestureRecognizers,对详情页面的跳转


    
        
    
 IOS下运行程序

 

我们实现了查看用户详情,并且可以修改

优化代码 

  • 我们要在数据库和视图模型之间提供一个抽象层,它能使项目有不同的模块区分,更明确的分离开。

  • 在项目中往往需要对多张表进行操作,我们创建一个泛型接口,来抽象对数据库中CURD。

  • 在Models中添加一个IRepository.cs文件

public interface IRepository where T:class
{
     Task GetByIdAsync(int id);
     
     Task> GetAllAsync();
     
     Task AddAsync(T item);
     
     Task UpdateAsync(T item);
     
     Task DeleteAsync(T item);
    
}
  • 实现接口

public class UserRepository: IRepository
{
    private readonly DbSet DbSet;

    private readonly UserContext Context;
    
    public UserRepository(UserContext context)
    {
        Context = context;
        DbSet = Context.Set();
    }

    public async Task GetByIdAsync(int id)
    {
        return await Task.Run(() => DbSet.Find(id));
    }

    public async Task> GetAllAsync()
    {
        return await Task.Run(() => DbSet.ToList());
    }

    public async Task AddAsync(User item)
    {
        DbSet.Add(item);
        await Task.CompletedTask;
    }

    public async Task UpdateAsync(User item)
    {
        DbSet.Attach(item);
        Context.Entry(item).State = EntityState.Modified;
        await Task.CompletedTask;
    }

    public async Task DeleteAsync(User item)
    {
        DbSet.Remove(item);
        await Task.CompletedTask;
    }
  • 创建工作单元,它的作用主要是作用于不同的数据表。

public class DbUnitOfWork:IDisposable,IUnitOfWork
{
    readonly UserContext Context=new UserContext();

    private IRepository userRepository;
    
    public IRepository Items => userRepository ??= new UserRepository(Context);
    
    public void Dispose()
    {
        Context.Dispose();
    }


    public async Task SaveAsync()
    {
        await Task.Run(() => Context.SaveChangesAsync());
    }
}

public interface IUnitOfWork where T : class {
    IRepository Items { get; } 
    Task SaveAsync(); 
}
修改MainViewModel
  • LoadUserAsync

[RelayCommand]
private async Task LoadUsersAsync()
{
    using var uniOfWork = new DbUnitOfWork();
    Users = new ObservableCollection(await uniOfWork.Items.GetAllAsync());
    Refreshing = false;
}
  • DeleteUserAsync

[RelayCommand]
private async Task DeleteUser(User user)
{
    using var uniOfWork = new DbUnitOfWork();
    await uniOfWork.Items.DeleteAsync(user);
    await uniOfWork.SaveAsync();
    
    Users.Remove(user);
}
修改CustomerEditViewModel
  • SaveAsync

[RelayCommand]
private async Task SaveAsync()
{
    using var unitOfWork = new DbUnitOfWork();


    if (IsNewItem)
        await unitOfWork.Items.AddAsync(ItemUser);
    else
        await unitOfWork.Items.UpdateAsync(ItemUser);
    

    await unitOfWork.SaveAsync();
    
    await ParentRefreshAction(ItemUser);
    
    await Shell.Current.GoToAsync("..");
}
修改UserDetailViewModel
  • ShowEditFormAsync

[RelayCommand]
async Task ShowEditFormAsync()
{
    using var unitOfWork = new DbUnitOfWork();
    
    var editedItem = await unitOfWork.Items.GetByIdAsync(ItemUser.Id);

    await Shell.Current.GoToAsync(nameof(UserEditPage),
        parameters: new Dictionary
        {
            { "ParentRefreshAction", (Func)ItemEditedAsync },
            { "Item", editedItem }
        });
}

 数据库验证错误

  • 很多时候我们需要对用户输入的数据进行验证,有很多方法和形式,我们来看看在数据库层面如何做错误验证处理。并反馈在页面给用户。

  • 我们在UserContext中进行简单的数据约束

  • 使用try catch来捕获异常

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //用户邮箱唯一
    modelBuilder.Entity().HasIndex(u => u.Email).IsUnique();
    //用户名不能为空
    modelBuilder.Entity().Property(u => u.Name).IsRequired();
    //初始化数据
    modelBuilder.Entity().HasData(new User
    {
        Id = 1,
        Name = "张三",
        Email = "张三@163.com",
        Phone = "123456789"
    });
    
    base.OnModelCreating(modelBuilder);
}
修改MainViewModel
  • DeleteCustomerAsync

[RelayCommand]
private async Task DeleteUserAsync(User user)
{
    using var uniOfWork = new DbUnitOfWork();
    try
    {
        await uniOfWork.Items.DeleteAsync(user);
        await uniOfWork.SaveAsync();
    }
    catch (Exception ex)
    {
        await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
        return;
    }

    Users.Remove(user);
}
修改UserEditViewModel
  • SaveAsync

[RelayCommand]
private async Task SaveAsync()
{
    using var unitOfWork = new DbUnitOfWork();

    try
    {
        if (IsNewItem)
            await unitOfWork.Items.AddAsync(ItemUser);
        else
            await unitOfWork.Items.UpdateAsync(ItemUser);
    

        await unitOfWork.SaveAsync();

    }
    catch (Exception ex)
    {
        await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
        return;
    }
    await ParentRefreshAction(ItemUser);
    
    await Shell.Current.GoToAsync("..");
}
IOS下运行程序
  • 这里如果用户没有添加用户名,程序会提出错误信息。

在UI中验证数据 

  • 程序中,在将数据提交到数据库之前可以在UI层执行某些验证规则,可以在用户保存某些修改时告知用户,从而改善用户体验。

修改UserEditViewModel
  • 在编辑用户页面我们添加对用户名和邮箱的验证,并关联到保存命令上。

[NotifyCanExecuteChangedFor(nameof(SaveCommand))] [ObservableProperty]
private bool isEmailValid;

[NotifyCanExecuteChangedFor(nameof(SaveCommand))] [ObservableProperty]
private bool isNameValid;

bool CanSave() => IsEmailValid&& IsNameValid;


[RelayCommand(CanExecute = nameof(CanSave))]
private async Task SaveAsync()
{
    using var unitOfWork = new DbUnitOfWork();

    try
    {
        if (IsNewItem)
            await unitOfWork.Items.AddAsync(ItemUser);
        else
            await unitOfWork.Items.UpdateAsync(ItemUser);
    

        await unitOfWork.SaveAsync();

    }
    catch (Exception ex)
    {
        await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
        return;
    }
    await ParentRefreshAction(ItemUser);
    
    await Shell.Current.GoToAsync("..");
}

 

  • 在UserEditPage文件中,我们添加一个样式来反馈给用户,并使用工具包中的ValidationBehavior来进行验证和绑定命令。

  • 这个验证很简单,当用户输入的内容不满足条件时,会使用我们设置的样式颜色来显示,保存按钮无法点击,当满足条件时颜色变为正常并可以保存内容。

  • 
        
            
        
        
            
        
        
        
            
                
                    
                        
                    
                
                
                
                    
                      //验证是否时正常的邮箱格式
                        
                    
                
                
               
                

IOS下运行程序 

你可能感兴趣的:(Maui学习笔记,学习,笔记,ios,c#,sqlite)