循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据
有时候,一些数据的录入可能需要使用表格直接录入会显得更加方便快捷,这种情况有时候也是由于客户使用习惯而提出,本篇随笔介绍在WPF应用端上使用DataGrid来直接新增、编辑、保存数据的处理。
录入数据的时候,我们都采用在一个窗体界面中,根据不同内容进行录入,但是有时候涉及主从表的数据录入,从表的数据有时候为了录入方便,也会通过表格控件直接录入。在Winform开发的时候,我们很多时候可以利用表格GridControl控件来直接录入数据;在BS的Vue&Elment前端项目中,也可以利用第三方组件vxe-table直接录入表格数据。
在不同的前端处理中,对于数据直接录入的处理,我写了一些随笔,可以参考。
在Winform界面中,也可以实现基于表格数据的直接录入,如下随笔所示《在DevExpress程序中使用Winform分页控件直接录入数据并保存》、《在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能》、《在Winform中直接录入表格数据和在Vue&Elment中直接录入表格数据的比较》。
如在Vue的前端录入中,也可以实现数据直接录入的,详细可以参考随笔介绍《在Vue前端界面中,几种数据表格的展示处理,以及表格编辑录入处理操作》。
在Bootstrap的Web开发中,也可以使用控件实现数据表格的直接录入处理,如随笔介绍《在Bootstrap开发框架中使用dataTable直接录入表格行数据》。
1、在DataGrid上直接编辑保存数据的界面效果
一般情况下,我们可能会利用新的窗口来承载数据表格的内容,这样展现的方式会比较灵活,也比较丰富一些,如下界面所示。

有时候,我们也会采用直接录入数据的方式,来快速直接录入一些简单的数据,如下界面所示。

对于新增或者编辑,单击某行记录的时候,会进行编辑处理,如下界面所示。

但是,往往很多数据不是简单的录入,我们可能会涉及一些下拉列表,以及一些自定义的处理,如下所示。

普通下拉列表的处理

也可能是一些特殊的自定义处理,如选择图标的操作。

当然还有一些单选框、复选框等处理,那些也是类似处理,不在赘述。
2、实现过程和思路
大致了解了一些常规的直接编辑处理,我们来看看如何具体实现上面的效果的。
一般默认的DataGrid就是可以编辑内容的,或者你把只读属性IsReadOnly设置为false即可,如下: IsReadOnly="False"
编辑情况下,我们列表就不用设置复选框来勾选,而采用序号显示的方式,如下设置代码。
<DataGrid
x:Name="grid"
hc:DataGridAttach.ShowRowNumber="True"
AddingNewItem="grid_AddingNewItem"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserSortColumns="False"
GridLinesVisibility="Vertical"
HeadersVisibility="All"
ItemsSource="{Binding ViewModel.MenuItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
RowHeaderWidth="60"
SelectionMode="Extended"
VerticalGridLinesBrush="AliceBlue">
<!--<DataGrid.RowHeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
</DataTemplate>
</DataGrid.RowHeaderTemplate>-->
对于直接编辑的数据列表,需要采用ObservableCollection<T>的集合处理,如果采用List<T>是不可以正常处理的。
/// <summary>
/// 编辑的数据列表
/// </summary>
[ObservableProperty]
private ObservableCollection<MenuInfo>? menuItems;
定义好集合后,我们对其中任何记录的处理,都是可以反映到界面上的了。如果需要新增记录,我们添加一个新的记录到上面的集合中即可体现到界面上了。
例如我们通过按钮新增一条记录,触发一个命令处理即可。
<Button
Width="80"
Height="40"
Margin="5"
hc:IconElement.Geometry="{StaticResource AddGeometry}"
Command="{Binding AddNewCommand}"
Content="新增"
Style="{StaticResource ButtonInfo}" />
其中AddNewCommand的命令操作,就是往上面的集合中添加一个记录即可体现到界面上了。
/// <summary>
/// 新增记录
/// </summary>
[RelayCommand]
private void AddNew()
{
this.ViewModel!.MenuItems!.Add(new MenuInfo()
{
SystemType_ID = App.ViewModel.SystemType,
//初始化
});
}
界面效果如下所示。

一般的列,编辑状态下就是文本框的处理,如下代码所示。
<DataGrid.Columns>
<DataGridTextColumn
MinWidth="50"
Binding="{Binding Name}"
Header="显示名称" />

而如果要自定义常规显示和编辑状态下的不同,那么就需要自定义模板<DataGridTemplateColumn.CellTemplate>、<DataGridTemplateColumn.CellEditingTemplate>来处理,如下所示。
例如对于图标列,两个模板内容不同的。
<DataGridTemplateColumn Width="80" Header="图标">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ui:SymbolIcon
Width="32"
FontSize="32"
Symbol="{Binding Icon}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel
MinWidth="200"
Background="{DynamicResource RegionBrush}"
Orientation="Horizontal">
<ui:SymbolIcon
FontSize="24"
Foreground="red"
Symbol="{Binding Icon, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button
Command="{Binding Path=DataContext.SelectIconCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=SelectedItem}"
Content="选择图标"
Foreground="red"
Style="{StaticResource ButtonInfo.Small}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
编辑模板下的显示和常规的不一样,以便提供一些入口我们进行相应的处理。

这里通过按钮,触发一个选择图标的命令操作,如下代码所示。
/// <summary>
/// 选择图标
/// </summary>
[RelayCommand]
private void SelectIcon(MenuInfo info)
{
//基于 SymbolRegular 图标选择
var page = App.GetService<SymbolRegularSelectPage>();
page!.ViewModel.ResetData();//每次更新数据
if (page.ShowDialog() == true)
{
var item = page.ViewModel.SelectedItem;
info.Icon = item.Text;
}
}
而对于一些普通的下拉列表,我们通过提供数据源的方式来绑定列表即可。

根据实际情况初始化下拉列表的内容,如下所示。
/// <summary>
/// 初始化字典
/// </summary>
/// <returns></returns>
public async Task InitDictItem()
{
//初始化性别的列表
this.GenderItems = new List<CListItem>()
{
new("男"),
new("女")
};
this.IsExpandToYesNoItems = new List<CListItem>()
{
new() { Text="是", Value = "1"},
new() { Text="否", Value = "0"},
};
this.IsVisibleToYesNoItems = new List<CListItem>()
{
new() { Text="是", Value = "1"},
new() { Text="否", Value = "0"},
};
}
其中XAML中的下拉列表的处理代码如下所示。
<DataGridTemplateColumn Width="100" Header="是否可见">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Foreground="{Binding Visible, Converter={StaticResource NumberToColorReConverter}}" Text="{Binding Visible, Converter={StaticResource NumberToYesNoStrConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding DataContext.ViewModel.IsVisibleToYesNoItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
SelectedValue="{Binding Visible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Value"
Text="{Binding Visible, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NumberToYesNoStrConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
注意上面的绑定和选择路径,从而可以通过修改直接反映到集合上。
其中获取记录集合的处理操作代码如下所示。
public override async Task GetData()
{
//转换下分页信息
ConvertPagingInfo(); var result = await service.GetListAsync(this.PageDto);
if (result != null)
{
//this.MenuItems.Clear();
this.MenuItems = new ObservableCollection<MenuInfo>(result.Items?.ToList());
this.PagerInfo.RecordCount = result.TotalCount;
}
}
通过获得查询数据的处理,我们可以显示具体的页面信息。

<StackPanel Orientation="Horizontal">
<hc:Pagination
Margin="0,10,0,10"
DataCountPerPage="{Binding ViewModel.PagerInfo.PageSize}"
IsJumpEnabled="True"
MaxPageCount="{Binding ViewModel.PagerInfo.MaxPageCount}"
MaxPageInterval="5"
PageIndex="{Binding ViewModel.PagerInfo.CurrentPageIndex}">
<hc:Interaction.Triggers>
<hc:EventTrigger EventName="PageUpdated">
<hc:EventToCommand Command="{Binding ViewModel.PageUpdatedCommand}" PassEventArgsToCommand="True" />
</hc:EventTrigger>
</hc:Interaction.Triggers>
</hc:Pagination> <TextBlock
Margin="20,0,0,0"
VerticalAlignment="Center"
Text="共有记录数" />
<TextBlock
Margin="10,0,0,0"
VerticalAlignment="Center"
Foreground="Blue"
Text="{Binding ViewModel.MenuItems.Count}" />
<TextBlock
Margin="10,0,0,0"
VerticalAlignment="Center"
Text="/" />
<TextBlock
Margin="10,0,0,0"
VerticalAlignment="Center"
Foreground="Blue"
Text="{Binding ViewModel.PagerInfo.RecordCount}" />
<TextBlock
Margin="10,0,0,0"
VerticalAlignment="Center"
Text="条" />
</StackPanel>
而对于父级记录的处理,我们可以在里面增加一个下列树状列表的处理即可。
<DataGridTemplateColumn Width="150" Header="上级菜单">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Foreground="{Binding PID}" Text="{Binding PID, Converter={StaticResource MenuIdToNameConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<control:MenuControl Text="" Value="{Binding PID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
这样就可以弹出下列列表的信息了,这样我们可以绑定各种不同的树状数据集合,从而达到更好的编辑处理体验了。

数据记录的删除操作,我们可以判断集合对象的Id是否为空,来判断是否是新增的,然后采用不同的方式删除处理。
/// <summary>
/// 删除操作
/// </summary>
/// <param name="info">业务对象</param>
[RelayCommand]
private async Task Delete(MenuInfo info)
{
if (info == null) return;
if (info.Id.IsNullOrEmpty())
{
this.ViewModel.MenuItems!.Remove(info);
}
else
{
if (MessageDxUtil.ShowYesNoAndWarning("您确认删除该记录?") != System.Windows.MessageBoxResult.Yes)
return;
this.ViewModel.DeleteCommand.Execute(info);
}
}
而保存的时候,我们对记录进行新增或者修改即可,统一处理。
/// <summary>
/// 用于子类重写的保存更新操作
/// </summary>
/// <returns></returns>
protected virtual async Task<bool> InsertOrUpdate()
{
bool result = false;
if (this.MenuItems != null)
{
foreach (var item in this.MenuItems)
{
await service.InsertOrUpdateAsync(item, item.Id);
}
result = true;
}
return result;
}
以上就是我们在数据编辑的处理思路和常规的做法,如果您对界面有更高的要求,也可以和我一起讨论处理。
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(10) -- 在DataGrid上直接编辑保存数据的更多相关文章
- 基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发
我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码 ...
- 基于SqlSugar的开发框架循序渐进介绍(22)-- Vue3+TypeScript的前端工作流模块中实现统一的表单编辑和表单详情查看处理
在工作流页面中,除了特定的业务表单信息外,往往也需要同时展示通用申请单的相关信息,因此在页面设计的时候需要使用一些组件化的概念来实现动态的内容展示处理,本篇随笔介绍Vue3+TypeScript+El ...
- 推荐一个基于Vue2.0的的一款移动端开发的UI框架,特别好用。。。
一丶YDUI 一只注重审美,且性能高效的移动端&微信UI. 下面为地址自己研究去吧! 我的项目正在用,以前用的Mint-ui但是现在感觉还是这个好一点,官方给出的解释很清楚,很实用. 官方地址 ...
- 基于yaf框架和uploadify插件,做的一个导入excel文件,查看并保存数据的功能
思路: 1.首先,页面前端,上传附件,提交给后台,并带一个随机性的参数(可以用时间戳): 2.后端接收附件,做一系列的逻辑处理,无误后,将对应的文件存储在上传的目录下: 3.然后前端,上传附件成功后, ...
- 基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理
我们在设计数据库表的时候,往往为了方便,主键ID一般采用字符串类型或者GUID类型,这样对于数据库表记录的迁移非常方便,而且有时候可以在处理关联记录的时候,提前对应的ID值.但有时候进行数据记录插入的 ...
- 基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转
在前面随笔,我们介绍过这个基于SqlSugar的开发框架,我们区分Interface.Modal.Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface ...
- 基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口
在基于SqlSugar的开发框架中,我们设计了一些系统服务层的基类,在基类中会有很多涉及到相关的数据处理操作的,如果需要跟踪具体是那个用户进行操作的,那么就需要获得当前用户的身份信息,包括在Web A ...
- 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...
- 基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理
在早期的随笔就介绍过,把常规页面的内容拆分为几个不同的组件,如普通的页面,包括列表查询.详细资料查看.新增资料.编辑资料.导入资料等页面场景,这些内容相对比较独立,而有一定的代码量,本篇随笔介绍基于V ...
- 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用
在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...
随机推荐
- 一文读懂什么是AIGC?
目录 AIGC概念 AIGC发展历史 在早期萌芽阶段(1950s~1990s) 在沉淀累积阶段(1990s~2010s) 在快速发展阶段(2010s~至今) ChatGPT AIGC能做什么? 电子商 ...
- Blazor前后端框架Known-V1.2.2
V1.2.2 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. 概述 基于C#和Blazor实现的快速开发框架,前后端分离,开箱即用. 跨平台,单 ...
- 基于JavaFX的扫雷游戏实现(二)——游戏界面
废话环节:看过上期文章的小伙伴现在可能还是一头雾水,怎么就完成了核心内容,界面呢?哎我说别急让我先急,博主这不夜以继日地肝出了界面部分嘛.还是老规矩,不会把所有地方都照顾到,只挑一些有代表性的内容 ...
- 4.2 针对PE文件的扫描
通过运用LyScript插件并配合pefile模块,即可实现对特定PE文件的扫描功能,例如载入PE程序到内存,验证PE启用的保护方式,计算PE节区内存特征,文件FOA与内存VA转换等功能的实现,首先简 ...
- 为什么使用ioutil.ReadAll 函数需要注意
1. 引言 当我们需要将数据一次性加载到内存中,ioutil.ReadAll 函数是一个方便的选择,但是ioutil.ReadAll 的使用是需要注意的. 在这篇文章中,我们将首先对ioutil.Re ...
- iOS Block笔记总结
前言: 对block的简单笔记总结, 1.本质: 封装了函数调用和函数调用环境的对象 2.block结构: 3.block捕获变量: 由于需要跨函数访问变量,所以需要捕获变量,(防止访问时已被销毁) ...
- EaselJS 源码分析系列--第三篇
这一篇分析另外四个稍显高级的显示类 -- Sprite.Movieclip.DOMElement.BitmapText SpriteSheet SpriteSheet 比较简单 它继承自 EventD ...
- EC600U-4G模组,连接阿里云测试服务器和物联网平台
原博主视频:https://www.bilibili.com/video/BV1yT4y1P7Gw?share_source=copy_web 连接阿里云服务器 !!需要公网ip(服务器)才能远程,不 ...
- 【WebGL系列-04】清除缓冲区并绘制图形
清除缓冲区并绘制图形 前文中已经准备好了webgl程序和绘制所用的数据,但是在绘制图像之前,还要对画布进行处理. 清除缓冲区 由于图像的绘制是一帧一帧绘制,每一帧针对当前的状态,计算屏幕上每个像素的颜 ...
- VMware 备份操作系统
在VMware 中备份方式有两种:快照和克隆. 快照:又称还原点,就是保存在拍快照时系统的状态,包含所有内容.在之后的使用中,随时都可以恢复.[短期备份,需要频繁备份时,使用该方法.操作的虚拟系统一般 ...