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

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

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

创建项目 

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

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

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

public partial class UserDetailViewModel:ObservableObject,IQueryAttributable
{[ObservableProperty] private User itemUser;public Func<User, Task> 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<string, object>{{ "ParentRefreshAction", (Func<User, Task>)ItemEditedAsync },{ "Item", editedItem }});}async Task ItemEditedAsync(User user) {ItemUser = user;await ParentRefreshAction(user);}public virtual void ApplyQueryAttributes(IDictionary<string, object> query){if (query.TryGetValue("Item",out object currentItem)){ItemUser = (User)currentItem;}if (query.TryGetValue("ParentRefreshAction",out object parentRefreshAction)){ParentRefreshAction = (Func<User, Task>)parentRefreshAction;}query.Clear();}
}
  • 创建用户详情页面,用来显示用户的全部信息。

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

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:vm="clr-namespace:MauiApp3.ViewModels"Title="用户详情"x:Class="MauiApp3.Views.UserDetailPage"><ContentPage.BindingContext><vm:UserDetailViewModel/></ContentPage.BindingContext><ContentPage.ToolbarItems><ToolbarItem Text="编辑" Command="{Binding ShowEditFormCommand}"/></ContentPage.ToolbarItems><VerticalStackLayout><Label Text="{Binding ItemUser.Id}" FontSize="Large"/><Label Text="{Binding ItemUser.Name}" FontSize="Large"/><Label Text="{Binding ItemUser.Phone}" FontSize="Large"/><Label Text="{Binding ItemUser.Email}" FontSize="Large"/></VerticalStackLayout>
</ContentPage>
  • 更新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<string, object> query) {if (query.TryGetValue("IsNewItem", out object isNew)) {IsNewItem = (bool)isNew;}base.ApplyQueryAttributes(query);}
}

 

  • 修改MainViewModel。

public partial class MainViewModel:ObservableObject
{[ObservableProperty]ObservableCollection<User> users;[ObservableProperty] private bool refreshing;[RelayCommand]private async Task LoadUsersAsync(){await Task.Run(() =>{using var context = new UserContext();Users = new ObservableCollection<User>(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<string, object>{{"ParentRefreshAction",(Func<User,Task>)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<string, object>{{"ParentRefreshAction",(Func<User,Task>)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,对详情页面的跳转

<Grid RowDefinitions="40,40"ColumnDefinitions="*,*"Padding="10"><Grid.GestureRecognizers><TapGestureRecognizerCommand="{Binding Path=BindingContext.ShowDetailFormCommand,Source={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContentPage}}}"CommandParameter="{Binding}"/></Grid.GestureRecognizers>
 IOS下运行程序

 

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

优化代码 

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

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

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

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

public class UserRepository: IRepository<User>
{private readonly DbSet<User> DbSet;private readonly UserContext Context;public UserRepository(UserContext context){Context = context;DbSet = Context.Set<User>();}public async Task<User> GetByIdAsync(int id){return await Task.Run(() => DbSet.Find(id));}public async Task<IEnumerable<User>> 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<User>
{readonly UserContext Context=new UserContext();private IRepository<User> userRepository;public IRepository<User> Items => userRepository ??= new UserRepository(Context);public void Dispose(){Context.Dispose();}public async Task SaveAsync(){await Task.Run(() => Context.SaveChangesAsync());}
}public interface IUnitOfWork<T> where T : class {IRepository<T> Items { get; } Task SaveAsync(); 
}
修改MainViewModel
  • LoadUserAsync

[RelayCommand]
private async Task LoadUsersAsync()
{using var uniOfWork = new DbUnitOfWork();Users = new ObservableCollection<User>(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);elseawait 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<string, object>{{ "ParentRefreshAction", (Func<User, Task>)ItemEditedAsync },{ "Item", editedItem }});
}

 数据库验证错误

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

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

  • 使用try catch来捕获异常

protected override void OnModelCreating(ModelBuilder modelBuilder)
{//用户邮箱唯一modelBuilder.Entity<User>().HasIndex(u => u.Email).IsUnique();//用户名不能为空modelBuilder.Entity<User>().Property(u => u.Name).IsRequired();//初始化数据modelBuilder.Entity<User>().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);elseawait 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);elseawait 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来进行验证和绑定命令。

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

  • <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:vm="clr-namespace:MauiApp3.ViewModels;assembly=MauiApp3"xmlns:toolkit = "http://schemas.microsoft.com/dotnet/2022/maui/toolkit"x:Class="MauiApp3.Views.UserEditPage"Title="新用户"><ContentPage.BindingContext><vm:UserEditViewModel/></ContentPage.BindingContext><ContentPage.Resources><Style TargetType="Entry" x:Key="invalidEntryStyle"><Setter Property="TextColor" Value="Red"></Setter></Style></ContentPage.Resources><Grid><VerticalStackLayout VerticalOptions="Start"><Entry Placeholder="用户名"Text="{Binding ItemUser.Name}"><Entry.Behaviors><toolkit:TextValidationBehavior InvalidStyle="{StaticResource invalidEntryStyle}"IsValid="{Binding IsNameValid}"Flags="ValidateOnValueChanged,ValidateOnAttaching"//内容长度不能小于5MinimumLength="5"/></Entry.Behaviors></Entry><Entry Placeholder="电话"Text="{Binding ItemUser.Phone}"/><Entry Placeholder="Email"Text="{Binding ItemUser.Email}"ReturnCommand="{Binding SaveCommand}"><Entry.Behaviors>//验证是否时正常的邮箱格式<toolkit:EmailValidationBehaviorInvalidStyle="{StaticResource invalidEntryStyle}"IsValid="{Binding IsEmailValid}"Flags="ValidateOnValueChanged,ValidateOnAttaching"/></Entry.Behaviors></Entry><Button Text="保存" Command="{Binding SaveCommand}"/></VerticalStackLayout><ActivityIndicator VerticalOptions="Center"HorizontalOptions="Center"IsRunning="{Binding SaveCommand.IsRunning}"/></Grid>
    </ContentPage>

IOS下运行程序 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/9802.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux文件原生操作

Linux 中一切皆文件&#xff0c;那么 Linux 文件是什么&#xff1f; 在 Linux 中的文件 可以是&#xff1a;传统意义上的有序数据集合&#xff0c;即&#xff1a;文件系统中的物理文件 也可以是&#xff1a;设备&#xff0c;管道&#xff0c;内存。。。(Linux 管理的一切对象…

HttpClient学习

目录 一、概述 二、HttpClient依赖介绍 1.导入HttpClient4依赖 2.或者导入HttpClient5依赖 3.二者区别 三、HttpClient发送Get请求和Post请求测试 (一)通过HttpClient发送Get请求 (二)通过HttpClient发送Post请求 一、概述 HttpClient是 Apache 软件基金会提供的一…

【重生之我在学习C语言指针详解】

目录 ​编辑 --------------------------------------begin---------------------------------------- 引言 一、指针基础 1.1 内存地址 1.2 指针变量 1.3 指针声明 1.4 取地址运算符 & 1.5 解引用运算符 *** 二、指针运算 2.1 指针加减运算 2.2 指针关系运算 三…

< OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)

目标&#xff1a; 使用 日本阿里云的 VPM 传输文件。 暂时方案&#xff1a; 使用 主机JPN 下载 https://huggingface.co/ 上模型从 JPN 放到 度狗上在家里从狗度下载 为了减少编程&#xff0c;尽量使用现在软件 &#xff0c;就找到 GitHub - qjfoidnh/BaiduPCS-Go: iikira…

98.1 AI量化开发:长文本AI金融智能体(Qwen-Long)对金融研报大批量处理与智能分析的实战应用

目录 0. 承前1. 简介1.1 通义千问(Qwen-Long)的长文本处理能力 2. 基础功能实现2.1 文件上传2.2 单文件分析2.3 多文件分析 3. 汇总代码&运行3.1 封装的工具函数3.2 主要功能特点3.3 使用示例3.4 首次运行3.5 运行结果展示 4. 注意事项4.1 文件要求4.2 错误处理机制4.3 最佳…

Linux环境基础开发工具的使用(apt, vim, gcc, g++, gbd, make/Makefile)

目录 什么是软件包 Linux 软件包管理器 apt 认识apt 查找软件包 安装软件 如何实现本地机器和云服务器之间的文件互传 卸载软件 Linux编辑器 - vim vim的基本概念 vim下各模式的切换 vim命令模式下各指令汇总 vim底行模式个指令汇总 Linux编译器 - gcc/g gcc/g的作…

deepseek R1的确不错,特别是深度思考模式

deepseek R1的确不错&#xff0c;特别是深度思考模式&#xff0c;每次都能自我反省改进。比如我让 它写文案&#xff1a; 【赛博朋克版程序员新春密码——2025我们来破局】 亲爱的代码骑士们&#xff1a; 当CtrlS的肌肉记忆遇上抢票插件&#xff0c;当Spring Boot的…

SpringBoot源码解析(八):Bean工厂接口体系

SpringBoot源码系列文章 SpringBoot源码解析(一)&#xff1a;SpringApplication构造方法 SpringBoot源码解析(二)&#xff1a;引导上下文DefaultBootstrapContext SpringBoot源码解析(三)&#xff1a;启动开始阶段 SpringBoot源码解析(四)&#xff1a;解析应用参数args Sp…

基于Django的个人博客系统的设计与实现

【Django】基于Django的个人博客系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统采用Python作为主要开发语言&#xff0c;结合Django框架构建后端逻辑&#xff0c;并运用J…

Vue-day2

7.Vue的生命周期 mounted函数&#xff1a;在页面加载完毕时&#xff0c;发送异步请求&#xff0c;加载数据&#xff0c;渲染页面 createApp({date(){},methods:{},mounted:function(){console.log(Vue挂载完毕&#xff0c;发送请求获取数据)} }).mount(#{app}) 8.ajax函数库…

SSM-MyBatis-总结

文章目录 一、Hello MyBatis1.1 流程1.2 总结 二、Crud 的一些注意点三、参数传递3.1 #{ } VS ${ }3.2 单、复参数传递&#xff08;1&#xff09;单参数&#xff08;2&#xff09;多参数 -- Param&#xff08;3&#xff09;总结 四、查询结果返回--结果封装4.1 ResultType 一般…

全面解析文件上传下载删除漏洞:风险与应对

在数字化转型的时代&#xff0c;文件上传、下载与删除功能已经成为各类应用程序的标准配置&#xff0c;从日常办公使用的协同平台&#xff0c;到云端存储服务&#xff0c;再到社交网络应用&#xff0c;这些功能在给用户带来便捷体验、显著提升工作效率的同时&#xff0c;也隐藏…

GSI快速收录服务:让你的网站内容“上架”谷歌

辛苦制作的内容无法被谷歌抓取和展示&#xff0c;导致访客无法找到你的网站&#xff0c;这是会让人丧失信心的事情。GSI快速收录服务就是为了解决这种问题而存在的。无论是新上线的页面&#xff0c;还是长期未被收录的内容&#xff0c;通过我们的技术支持&#xff0c;都能迅速被…

JavaScript

书写位置 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>/*C…

Go的内存逃逸

Go的内存逃逸 内存逃逸是 Go 语言中一个重要的概念&#xff0c;指的是本应分配在栈上的变量被分配到了堆上。栈上的变量在函数结束后会自动回收&#xff0c;而堆上的变量需要通过垃圾回收&#xff08;GC&#xff09;来管理&#xff0c;因此内存逃逸会增加 GC 的压力&#xff0…

python学opencv|读取图像(四十九)原理探究:使用cv2.bitwise()系列函数实现图像按位运算

【0】基础定义 按位与运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;全1取1&#xff0c;其余取0。 按位或运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;有1取1&#xff0c;其余取0。 按位异或运算&#xff1a; 两个等长度二进制数上下对齐&#xff0c;相…

图论——最小生成树的扩展应用

最小生成树相关原理 acwing1146.新的开始 假设存在一个“超级发电站” 在每一个矿井修发电站相当于从这个“超级发电站”到各个矿井连一条长度为 v [ i ] v[i] v[i]的边。 这样一来这就是一个最短路的模板题。 #include <iostream> #include <cstring> using na…

供应链系统设计-供应链中台系统设计(十)- 清结算中心概念片篇

综述 我们之前在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述文章中针对中台到底是什么进行了描述&#xff0c;对于中台的范围也进行划分&#xff0c;如下图所示&#xff1a; 关于商品中心&#xff0c;我们之前用4篇文章介绍了什么是商品中…

Git图形化工具【lazygit】

简要介绍一下偶然发现的Git图形化工具——「lazygit」 概述 Lazygit 是一个用 Go 语言编写的 Git 命令行界面&#xff08;TUI&#xff09;工具&#xff0c;它让 Git 操作变得更加直观和高效。 Github地址&#xff1a;https://github.com/jesseduffield/lazygit 主要特点 主要…

为大模型提供webui界面的利器:Open WebUI 完全本地离线部署deepseek r1

为大模型提供webui界面的利器&#xff1a;Open WebUI Open WebUI的官网&#xff1a;&#x1f3e1; Home | Open WebUI 开源代码&#xff1a;WeTab 新标签页 Open WebUI是一个可扩展、功能丰富、用户友好的自托管AI平台&#xff0c;旨在完全离线运行。它支持各种LLM运行程序&am…