当前位置:编程学堂 > 一步步介绍基于CommunityToolkit.Mvvm和HandyControl的WPF应用开发(4)

一步步介绍基于CommunityToolkit.Mvvm和HandyControl的WPF应用开发(4)

  • 发布:2023-10-05 08:23

在我们设计软件的很多地方,我们都看到需要导入导出表格数据,主要是为了方便客户快速的数据处理和共享功能方面,本文介绍了基于WPF的DataGrid数据的导入导出操作。

1。系统接口设计

在实现数据导入导出功能之前,我们需要在主界面上为客户提供相关的操作按钮。如下界面所示,列表顶部提供导入Excel、导出PDF、导出Excel。

由于这些操作函数基本上每个页面模块都会用到,所以我们尽量将它们抽象到基类中,并提供通用的处理操作。如果存在差异,它们也可以被某些属性或事件方法覆盖。实现它的方法。

所以我们在Xaml中定义按钮时,基本上都是调用视图模型方法进行泛化处理,如下代码所示。

<按钮
边距="5"
hc:IconElement.Geometry="{StaticResource t_import}"
Command="{绑定 ImportExcelCommand}"
内容="导入Excel"
样式="{静态资源按钮警告}"/>
<按钮
边距="5"
hc:IconElement.Geometry="{静态资源保存几何}"Command="{绑定ViewModel.ExportPdfCommand}"
CommandParameter="用户信息列表"
内容="导出PDF"
样式="{静态资源按钮成功}"/>
<按钮
边距="5"
hc:IconElement.Geometry="{静态资源保存几何}"
Command="{绑定ViewModel.ExportExcelCommand}"
CommandParameter="用户信息列表"
内容="导出Excel"
样式="{静态资源按钮成功}" />

导入的处理操作函数ImportExcelComand的定义如下(注意这里声明了RelayCommand),代码会自动生成Command后缀的Command方法。

///
/// 将内容导出到 Excel
/// 
 [继电器命令]
私有 void ImportExcel()
{
var页面 = App.GetService<ImportExcelData>();
页!.ViewModel.Items?.Clear();page!.ViewModel.TemplateFile = $"系统用户信息-Template.xls";
页!.OnDataSave -= ExcelData_OnDataSave;
页!.OnDataSave += ExcelData_OnDataSave;
// 导航至指定页面
ViewModel.Navigate(typeof(ImportExcelData));
}

其中ImportExcelData 是我们定义的通用导入页面表单类。这里我们只需要实现一些属性设置(根据不同的子类进行调整,后期可以用代码生成工具生成),以及一些事件用于子类延迟实现,从而实现自定义的数据处理功能。

我们将在下面详细说明批量导入处理的细节。

2。将数据导出到 Excel

导出数据到Excel在我们Winform端是很常见的,这里在WPF中也采用同样的处理方式。一般使用Excel的操作组件的封装类来实现,可以基于NPOI,也可以基于Aspose.Cell。您可以根据自己的需求实现简单的封装调用。

导出到Excel,首先需要弹出目录选择对话框选择目录,然后用它生成Excel文件,如下界面所示。

对于这个处理,由于WPF可以调用.net中的System.Windows.Forms,所以我们可以直接调用里面的对话框处理包。这个类来自我们Winform的UI公共类库部分。

在上一篇文章中,我们介绍了为了WPF开发的方便,我们设计了几个视图基类来减少代码处理。

对于不同的业务类,我们只需要根据实际情况生成对应的业务视图模型类即可。

我们可以把一般的导出操作放到这个视图基类BaseListViewModel中,如下代码所示。

///
///触发导出Excel处理命令
/// 
[继电器命令]
受保护 虚拟 异步任务 ExportExcel( string) 标题 = "列表数据")
{
var表 = 等待 这个.ConvertItems(这个.物品);
BaseExportExcel(表格,标题);
}

而其中,Excel提供了处理DataTable的通用方法。

///
/// 用于重写的基类函数,导出到 Excel
/// 
公共 虚拟 void BaseExportExcel(DataTable 表,字符串标题 = "列表数据")
{
字符串文件 = FileDialogHelper.SaveExcel(字符串.Format( ) "{0}.xls",标题 ));
if(!字符串.IsNullOrEmpty(文件))
{
尝试
{
字符串错误 = "";
AsposeExcelTools.DataTableToExcel2(表,文件,错误);
if(!字符串.IsNullOrEmpty(错误))
{
Messagedxutil.showerror(String.Format("导出excel错误:{0}",错误));
}
否则
{
if(MessageDxUtil.ShowYesNoAndTips("导出成功,是否要打开文件?" ) == System.Windows.MessageBoxResult 。是的)
{
Process.Start("explorer.exe",文件);
}
}
}
catch(例外情况)
{
LogTextHelper.Error(前);MessageDxUtil.ShowError(例如.Message);
}
}
}

FileDialogHelper.SaveExcel的代码如下。

///
/// 保存Excel对话框并返回保存完整路径
/// 
/// 
公共 静态 字符串 SaveExcel (字符串文件名,字符串初始目录)
{
返回保存("保存Excel",ExcelFilter,文件名,初始目录);
}
/// 
/// 弹出指定标题的保存文件对话框
/// 
/// 对话框标题
/// 后缀过滤
/// 默认文件名
/// 初始化目录
/// 
公共 静态字符串保存 (字符串标题,字符串过滤器,字符串文件名,字符串初始目录)
{
//多语言支持
标题 = JsonLanguage.Default.GetString(title);
var对话框=SaveFileDialog();dialog.Filter = 过滤器;
对话框.标题=标题;
dialog.FileName = 文件名;
对话框.RestoreDirectory = true;
if (!字符串.IsNullOrEmpty(initialDirectory))
{
dialog.InitialDirectory =initialDirectory;
}
if (dialog.ShowDialog() == DialogResult.OK)
{
返回dialog.FileName;
}
返回 .空;
}

其中SaveFileDialog是.net中System.Windows.Forms中的内容,可以直接被WPF调用。

DataTableToExcel2方法,是我们使用Aspose.Cell封装的调用,主要用于快速处理DataTable到Excel的操作封装。我们还可以使用其他Excel操作封装,比如NPOI等来实现这一点。

代码如下所示。

///
/// 将 DataTabel 转换为 Excel 文件
/// 
/// DataTable 对象
/// 目标文件路径,Excel文件的完整路径
/// 错误信息:返回错误信息,无错误返回“”
/// 公共 静态boolDataTableToExcel2(DataTable 数据表,string 文件路径,out string 错误)
{
错误= "";
var wb = new Aspose.Cells.Workbook();
尝试
{
if(数据表==null)
{
错误= "DataTableToExcel:数据表为空";
返回;
}
//为单元格添加样式
var style = wb.CreateStyle();
// 设置中心 
style.Horizo​​ntalAlignment = www.sychzs.cn;
//设置背景颜色
style.ForegroundColor = System.Drawing.Color.FromArgb(153, 204, 0);
style.Pattern =BackgroundType.Solid;
style.Font.IsBold = true;
int rowIndex = 0;
for (int i = 0; i < datatable.Columns.Count; i++)
{
DataColumn col = datatable.Columns[i];stringcolumnName = col.Caption??col.ColumnName;
wb.Worksheets[0].Cells[rowIndex, i].PutValue(columnName);
wb.Worksheets[0].Cells[rowIndex, i].SetStyle(style);
}
rowIndex++;
foreach(DataRow 行in 数据表.Rows)
{
for (int i = 0; i < datatable.Columns.Count; i++)
{
wb.Worksheets[0].Cells[rowIndex, i].PutValue(row[i].ToString());
}
rowIndex++;
}
for (int k = 0; k < datatable.Columns.Count; k++)
{
wb.工作表[0].AutoFitColumn(k, 0, 150);
}
wb.Worksheets[0].FreezePanes(1, 0, 1, datatable.Columns.Count);
wb.保存(文件路径);
返回;
}
catch(例外 e)
{
错误 = 错误 + " DataTableToExcel: " + e.Message;
返回;
}
}

导出Excel的内容如下图所示。另外导出文档的内容,我们可以用于导入的数据模板的。

我们可以根据需要设置要导出的列。

3。将数据导出为 PDF

同样,导出数据为PDF的处理操作类似。它还利用视图基类的封装方法来实现快速导出为PDF处理。下面是view基类中的实现方法。

///
/// 触发导出PDF处理命令
/// 
[继电器命令]
受保护 虚拟 异步任务 ExportPdf( string title = "列表数据")
{
var表 = 等待这个.ConvertItems (这个.物品);
BaseExportPdf(表格,标题);
}
/// 
///可重写的基类函数,导出PDF
/// 
公共 虚拟 void BaseExportPdf (DataTable表格,字符串标题 = "列出数据")
{
var pdfFile = FileDialogHelper.SavePdf();
if (!pdfFile.IsNullOrEmpty())
{

通过将List转换为常规DataTable进行处理,我们可以使用我们文章《在Winform分页控件中集成导出PDF文档的功能》中介绍的PDF导出功能将WPF数据导出为PDF。

上面的TextSharpHelper 是对itext7的封装,实现PDF导出处理。

介绍完相关的Nugget类后,封装它的辅助类代码如下。

///
///基于iText7的PDF导出处理
/// 
公共 静态 TextSharpHelper
{
/// 
/// 数据表转PDF方法
/// 
/// 标题内容
/// data表数据
/// PDF文件保存路径
///  是否横向打印,默认为true
/// 每页是否包含页眉信息
/// 头部对齐方式默认居中对齐
/// 标题字体大小
/// 行记录字体大小
///  头部固定高度,否则自适应
/// 公共 静态 bool ExportTableToPdf(string) 标题,数据表数据,字符串pdf文件,bool isLandscape = truebool includeHeader = true,iText.Layout.Properties.Hori zontalAlignment headerAlignment = iText.Layout.Properties.Horizo​​www.sychzs.cn, 浮动 headerFontSize = 9f, 浮动 rowFontSize = 9f, 浮动? headerFixHeight =  空)
{var writer = new PdfWriter(pdfFile);
PdfDocument pdf = new PdfDocument(作者);
pdf.SetDefaultPageSize(isLandscape ? PageSize.A4.Rotate() : PageSize.A4); //A4横向
var doc = new文档(pdf);//设置标题
if(!字符串.IsNullOrEmpty(标题))
{
var param = new 段落(标题)
.SetFontColor(www.sychzs.cn)
.SetBold() //粗体.SetFontSize(标题字体大小 + 5)
.SetTextAlignment(www.sychzs.cn); //居中
 doc.Add(param);
}
var表 = 表(data.Columns.Count)
.SetTextAlignment(www.sychzs.cn)
.SetVerticalAlignment(VerticalAlignment.MIDDLE)
.SetWidth(new UnitValue(UnitValue.PERCENT, 100));//缩放比例
 table.UseAllAvailableWidth();
//添加表头
foreach(DataColumn dc in data.Columns)
{
var title = !string.IsNullOrEmpty(dc.Caption) ? dc.Caption : dc.ColumnName;
var单元格 = new单元格().Add(new段落(标题))
.SetBold()
.SetVerticalAlignment(VerticalAlignment.MIDDLE)
.SetHorizo​​ntalAlignment(标题对齐)
.SetPadding(1)
.SetFontSize(headerFontSize);
if(headerFixHeight.HasValue)
{
cell.SetHeight(newUnitValue(UnitValue.POINT, headerFixHeight.Value));
}表.AddHeaderCell(单元格);
}
//插入数据
var colorWhite = Color.ConvertRgbToCmyk(iText.Kernel.Colors.WebColors.GetRGBColor("White" ));//系统.绘图.颜色.白色;
var colorEvent = iText.Kernel.Colors.WebColors.GetRGBColor("LightCyan"); // System.Drawing.Color。浅青色;
var EventRowBackColor = Color.ConvertRgbToCmyk(colorEvent);
for (int i = 0; i < data.Rows.Count; i++)
{
table.StartNewRow();//第一列开启新行
var backgroudColor = ((i % 2 == 0) ? colorWhite : EventRowBackColor);
for (int j = 0; j < data.Columns.Count; j++)
{
var text = data.Rows[i][j].ToString();
var单元格=单元格()
.SetBackgroundColor(背景颜色)
.SetFontSize(行字体大小)
.SetVerticalAlignment(VerticalAlignment.MIDDLE).Add(new段落(文本));
表.AddCell(单元格);
}
}
doc.Add(表);
pdf.关闭();
作者.关闭();
返回;
}
}

导出PDF的文档效果如下。

4。导入Excel数据

Excel数据导入,可以减少批量处理数据的难度和一一录入的繁琐界面。这是常见的操作方法。我们主要提供固定模板供客户下载并录入数据,然后提交进行批量处理。只需导入即可。

导入接口处理,我们这里涉及到一个通用的导入接口(类似于WInform端的接口),这样我们每个不同的业务导入流程都可以复用,只需要设置一些不同的属性和处理一些事件。就是这样,下面是大致的界面效果。

这里主要介绍一下它的设计方法。前面我们介绍过,在业务接口中调用时,如下代码所示。

///
/// 将内容导出到 Excel
/// 
[继电器命令]
私有void导入Excel()
{
var页面=App.GetService();页!.ViewModel.Items?.Clear();
页!.ViewModel.TemplateFile = $"系统用户信息模板.xls"  ;
页!.OnDataSave -= ExcelData_OnDataSave;
页!.OnDataSave += ExcelData_OnDataSave;
// 导航至指定页面
 ViewModel.Navigate(typeof(ImportExcelData));
}

这种通用形式的视图模型定义了模板的文件名、通用数据DataTable的集合、以及实现子类导入转换的事件。其视图模型类代码如下所示。

///
/// 查看批量导入Excel数据的模型基类
/// 
公共部分ImportExcelDataViewModel:BaseViewModel
{
[ObservableProperty]
私有 字符串 templateFile;
[ObservableProperty]
私有字符串导入文件路径;
[ObservableProperty]
私有数据表项目;

为了方便客户打开模板文件,以便可以用来录入Excel数据,我们可以在本地打开模板文件。

///
/// 打开模板文件
/// 
 [中继命令]
私有 void OpenFile()
{
if (!这个.TemplateFile.IsNullOrEmpty())
{
var realFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, this.TemplateFile);
if(文件.Exists(realFilePath))
{
Process.Start("explorer.exe", realFilePath);
}
否则
{
MessageDxUtil.ShowError($"找不到模板文件:{realFilePath}");
}
}
}

在通用导入页面的后台代码中,我们需要实现选择Excel后将数据显示到DataGrid、批量保存数据等一些操作。

///
///选择Excel文件后,显示Excel中的表格数据
/// 
 [中继命令]
私有 void BrowseExcel()
{
字符串 文件 = FileDialogHelper.OpenExcel();
if(!字符串.IsNullOrEmpty(文件))
{
这个.ViewModel.ImportFilePath = 文件;ViewData();
}
} 
///
/// 查看Excel文件并在界面上进行操作
/// 
私有 void查看数据()
{
if (这个.txtFilePath.Text == "")
{
MessageDxUtil.ShowTips("请选择指定的Excel文件");
返回;
}
尝试
{
var myDs = new DataSet();
字符串错误 = "";
AsposeExcelTools.ExcelFileToDataSet(这个.txtFilePath.Text,  出 myDs,错误);
这个.ViewModel.Items = myDs.Tables[0];
}
catch(例外情况)
{
LogTextHelper.Error(前);
MessageDxUtil.ShowError(例如.Message);
}
}

进口加工操作代码如下。

///
/// 批量保存数据到数据库
/// 
/// 
[继电器命令]
私有异步任务SaveData()
{if(ViewModel.Items == null || ViewModel.Items?.行?.Count == 0返回  CommonResult();
? )==系统。 Windows.MessageBoxResult.Yes)
{
var dt = 这个.ViewModel.Items;
foreach(DataRow dr in dt.Rows)
{
尝试
{
等待OnDataSave(dr);
}
catch(例外情况)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}
return new CommonResult(true, "操作成功");
}
return new CommonResult(false);
}

注意,我们这里使用了事件的处理,把数据的转换逻辑留给子类去实现的。

        /// 
/// 数据保存的事件
/// 
public event SaveDataHandler OnDataSave;

这样我们在用户信息的导入页面UserListPage.xaml.cs里面的代码就可以根据实际的情况进行实现事件了。

    /// 
/// 导出内容到Excel
/// 
    [RelayCommand]
private void ImportExcel()
{
var page = App.GetService();
page!.ViewModel.Items?.Clear();
page!.ViewModel.TemplateFile = $"系统用户信息-模板.xls";
page!.OnDataSave -= ExcelData_OnDataSave;
page!.OnDataSave += ExcelData_OnDataSave;
//导航到指定页面
ViewModel.Navigate(typeof(ImportExcelData));
}

这个事件的实现,主要就是把个性化的用户信息(用户信息模板里面定义的字段),转换为DataTable的行信息即可,如下代码所示。具体根据模板设计的情况进行修改即可。

具体就不再一一赘述,主要就是基类逻辑和具体实现分离,实现不同的业务功能处理即可。

以上即是我们一个列表通用页面里面,往往需要用到的通用性的导入、导出操作的介绍,希望对读者在开发WPF应用功能上有所启发,有所参考,善莫大焉。

相关文章

热门推荐