在 DevExpress XAF 中创建 Excel 报表
在 DevExpress 中使用 Excel 报表
引言
这篇教程介绍如何使用DevExpress创建Excel报表。它将向您展示如何创建一个自定义类来生成Excel文档,而不是使用DevExpress提供的传统报表。
使用代码
创建可以生成Excel表格的报表的最佳方法需要以下几个类。
首先是最重要的表单布局。此类将提供报表参数所需的所有信息。该类需要是非持久性的,以允许多个访问“点”,并包含基本构造函数。
[NonPersistent]
[CreatableItem(false)]
[ImageName("Action_Export_ToExcel")]
[ModelDefault("Caption", "Testing Report")]
public class ExampleReportForm : XPCustomObject
public ExampleReportForm(Session session) : base(session) { }
public override void AfterConstruction() { base.AfterConstruction()
然后是您需要的参数。例如,两个日期(起始日期和结束日期)。
注意
在本例中,我只将其作为示例,但在文档的其余部分,我将展示一个基本示例,其中没有参数,只有一个按钮来设置数据。
在这个例子中,我考虑了一个巧妙的设置,即“结束日期”会自动设置为“起始日期”后的7天。
[RuleRequiredField("RuleRequired.Example.FromDate", "From Date is required")]
[ImmediatePostData]
public DateTime FromDate
{
get
{
return _FromDate;
}
set
{
if (SetPropertyValue("FromDate", ref _FromDate, value) && !IsLoading && (_ToDate == DateTime.MinValue || _ToDate < _FromDate))
{
ToDate = FromDate.AddDays(7);
OnChanged("ToDate");
}
}
}
[RuleRequiredField("RuleRequired.Example.ToDate", "To Date is required")]
[ImmediatePostData]
public DateTime ToDate
{
get
{
return _ToDate;
}
set
{
if (SetPropertyValue("ToDate", ref _ToDate, value) && !IsLoading && (ToDate.CompareTo(FromDate) < 0))
{
SetPropertyValue("ToDate", ref _ToDate, FromDate);
}
}
}
然后,您需要创建一个类来处理非持久性类,因为DevExpress默认情况下不会处理它。正如您将看到的,我使用`OnCustomShowNavigationItem`方法来使DevExpress显示非持久性类,就像处理任何其他类一样。
public abstract class ShowNonPersistentObjectDetailViewFromNavigationControllerBase<nonpersistentobjecttype> : ViewController where NonPersistentObjectType : XPCustomObject
{
private const string DefaultReason = "ShowNonPersistentObjectDetailViewFromNavigationControllerBase is active";
public ShowNonPersistentObjectDetailViewFromNavigationControllerBase()
{
TargetObjectType = typeof(NonPersistentObjectType);
}
protected override void OnFrameAssigned()
{
base.OnFrameAssigned();
Frame.GetController<shownavigationitemcontroller>().CustomShowNavigationItem += OnCustomShowNavigationItem;
}
protected override void OnActivated()
{
base.OnActivated();
UpdateControllersState(false);
}
protected override void OnDeactivated()
{
base.OnDeactivated();
UpdateControllersState(true);
}
protected virtual void UpdateControllersState(bool flag)
{
//Frame.GetController<detailviewcontroller>().Active[DefaultReason] = flag;
Frame.GetController<modificationscontroller>().Active[DefaultReason] = flag;
Frame.GetController<deleteobjectsviewcontroller>().Active[DefaultReason] = flag;
Frame.GetController<newobjectviewcontroller>().Active[DefaultReason] = flag;
Frame.GetController<filtercontroller>().Active[DefaultReason] = flag;
Frame.GetController<viewnavigationcontroller>().Active[DefaultReason] = flag;
Frame.GetController<recordsnavigationcontroller>().Active[DefaultReason] = flag;
Frame.GetController<refreshcontroller>().Active[DefaultReason] = flag;
}
void OnCustomShowNavigationItem(object sender, CustomShowNavigationItemEventArgs e)
{
if (e.ActionArguments != null)
{
if (e.ActionArguments.SelectedChoiceActionItem != null)
{
//Debug.WriteLine(e.ActionArguments.SelectedChoiceActionItem.Id + " " + TargetObjectType.Name);
if (e.ActionArguments.SelectedChoiceActionItem.Id == (TargetObjectType.Name + "_DetailView"))
{
XPObjectSpace os = (XPObjectSpace)Application.CreateObjectSpace();
NonPersistentObjectType obj = CreateNonPersistemObject(os);
CustomizeNonPersistentObject(obj);
DetailView dv = Application.CreateDetailView(os, obj);
e.ActionArguments.ShowViewParameters.CreatedView = dv;
CustomizeShowViewParameters(e.ActionArguments.ShowViewParameters);
e.Handled = true;
}
}
}
}
protected virtual NonPersistentObjectType CreateNonPersistemObject(XPObjectSpace objectSpace)
{
return objectSpace.CreateObject<nonpersistentobjecttype>();
}
protected virtual void CustomizeShowViewParameters(ShowViewParameters parameters)
{
parameters.Context = TemplateContext.ApplicationWindow;
parameters.TargetWindow = TargetWindow.Current;
((DetailView)parameters.CreatedView).ViewEditMode = DevExpress.ExpressApp.Editors.ViewEditMode.Edit;
}
protected virtual void CustomizeNonPersistentObject(NonPersistentObjectType obj){ }
使用上面的类,我将展示导航控件的创建,这基本上只是报表独有的,用于调用上面的类,这允许您处理和查看非持久性类(如果您有多个类,它们都将以与以下代码相同的方式使用上面的代码),使用`ReportForm`类。
namespace Example.Module.Controllers
public class ExampleNavigationController : ShowNonPersistentObjectDetailViewFromNavigationControllerBase<examplereportform>
{
protected override void CustomizeNonPersistentObject(ExampleReportForm obj)
base.CustomizeNonPersistentObject(obj);
}
protected override void CustomizeShowViewParameters(ShowViewParameters parameters)
{
base.CustomizeShowViewParameters(parameters);
}
protected override void UpdateControllersState(bool flag)
{
base.UpdateControllersState(flag);
Frame.GetController<devexpress.expressapp.validation.allcontextsview.showallcontextscontroller>().Action.Active.SetItemValue("Hide", false);
}
现在显然需要视图控制器。在这里,我将使用最终类中获得的结果,将其压缩并允许将其下载为.xls文档。因此,创建一个视图控制器并在其上放置一个按钮,并设置以下属性。
在`TargetObjectType`中,使用下拉菜单选择您的`ReportForm` `(ExampleReportForm)`。然后显然是(名称)、标题、类别等,您可以随意设置。
现在是执行代码……我在这里只做了一个基本的查询,我获取静态数据。(毕竟这只是一篇基础教程文章;-))
Session session = ((XPObjectSpace)View.ObjectSpace).Session;
using (ExampleReport report = new ExampleReport())
{
TblPerson per = session.FindObject<tblpersonnel>(CriteriaOperator.Parse("OID = 23"));// Get Only set person's detail
string tempFileName = report.generateReport(per, null, session);
if (tempFileName != null)
{
string fileName = String.Format(@"Example-{0}.zip", String.Format("{0:yyyy-MM-dd_HH_mm_ss}", DateTime.Now)).Replace(" ", "");
tempFileName = FileEditor.CompressFile(tempFileName, fileName);
Page page = null;
if (Frame.Template is Page)
page = (Page)Frame.Template;
else
{
page = ((Control)Frame.Template).Page;
string script = String.Format(@"<script>var openedWindow = window.open('{0}', 'xls');</script>", tempFileName);
page.ClientScript.RegisterStartupScript(GetType(), "clientScript", script);
}
}
}
private void RunExampleReport_Execute(object sender, SimpleActionExecuteEventArgs e)
{
现在是乐趣所在的部分,所有实际逻辑都包含在此处。这将按集合而不是整体方式进行,如上所示……该类必须是`IDisposable`。
public class ExampleReport : IDisposable
public string generateReport(TblPerson user, UnitOfWork workUnit, Session session)
{
string xlsTemplate = String.Format(@"{0}ExampleReport.xlsx", Globals.xlsFilePath);
DirectoryInfo outputDir = new DirectoryInfo(String.Format(@"{0}", Globals.otherFilePath));
现在我使用了全局类(`Globals.xlsFilePath`和`Globals.otherFilePath`)作为路径,因此您只需要自己设置路径。
现在是用于保存所需信息的模板。
string tempFileName = null;
string webTempFileName = null;
if (File.Exists(xlsTemplate))
{
try
{
if ((session ?? workUnit) != null)
{
string fileName = String.Format(@"ExampleReport-{0}.xlsx", String.Format("{0:yyyy-MM-dd_HHmmss}", DateTime.Now)).Replace(" ", "");
tempFileName = String.Format(@"{0}{1}", Globals.otherFilePath, fileName);
webTempFileName = String.Format(@"{0}{1}", Globals.webTempFilePath, fileName);
现在是实际的代码,它将填充并获取通过控制器传递的值。我设置了一个XPCollection,使用数据库查询将所有数据放入集合中。
using (ExcelPackage excel = new ExcelPackage(new FileInfo(xlsTemplate), true))
{
XPCollection test = new XPCollection(workUnit ?? session, typeof(ProTask), CriteriaOperator.Parse("ReportedBy = ? && Status = ? && SysStatus <> ?", user, Status.Done, SystemStatus.Done));
ExcelWorksheet sheet = excel.Workbook.Worksheets["Testing"];
if (test != null && test.Count > 0)
{
int row = 3;
foreach (JobRef job in test)
{
int column = 1;
if (job!= null)
{
sheet.Cells["A3:D3"].Copy(sheet.Cells[String.Format("A{0}:D{0}", row)]);
sheet.Cells[row, column++].Value = String.Format("{0} {1}", test.Assign.FirstName, test.Assigned.Surname);
sheet.Cells[row, column++].Value = test.Subject;
sheet.Cells[row++, column].Value = test.Description;
}
}
Byte[] arr = excel.GetAsByteArray();
File.WriteAllBytes(tempFileName, arr);
}
return workUnit != null ? tempFileName : webTempFileName;
以下是`IDisposable`类的最终结果。
using DevExpress.Data.Filtering;
using DevExpress.Xpo;
using OfficeOpenXml;
using OwnNameSpace.Module.Classes.ProTaskClasses;
using OwnNameSpace.Module.CommonFunctions;
using System;
using System.IO;
namespace OwnNameSpace.Module.Classes.Reports.TestingReport
{
public class TestingReport : IDisposable
{
public TestingReport() { }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing) { }
}
~TestingReport()
{
Dispose(false);
}
public string generateReport(TblPersonnel user, UnitOfWork workUnit, Session session)
{
string xlsTemplate = String.Format(@"{0}TestingReport.xlsx", Globals.xlsFilePath);
DirectoryInfo outputDir = new DirectoryInfo(String.Format(@"{0}", Globals.otherFilePath));
if (!outputDir.Exists)
throw new Exception(String.Format(@"Folder: {0} does not exist! Cannot create temporary file", Globals.otherFilePath));
if (user == null)
throw new Exception("User supplied to TestingReport.generateReport() is null");
if (workUnit == null && session == null)
throw new Exception("UnitOfWork and Session supplied to TestingReport.generateReport() is null");
string tempFileName = null;
string webTempFileName = null;
if (File.Exists(xlsTemplate))
{
try
{
if ((session ?? workUnit) != null)
{
string fileName = String.Format(@"TestingReport-{0}.xlsx", String.Format("{0:yyyy-MM-dd_HHmmss}", DateTime.Now)).Replace(" ", "");
tempFileName = String.Format(@"{0}{1}", Globals.otherFilePath, fileName);
webTempFileName = String.Format(@"{0}{1}", Globals.webTempFilePath, fileName);
using (ExcelPackage excel = new ExcelPackage(new FileInfo(xlsTemplate), true))
{
using (XPCollection test = new XPCollection(workUnit ?? session, typeof(taskClass), CriteriaOperator.Parse("AssignedBy = ? && Status = ? && SysStatus <> ?", user, UserStatus.Completed, SystemStatus.Completed)))
{
ExcelWorksheet sheet = excel.Workbook.Worksheets["Testing"];
if (test != null && test.Count > 0)
{
int row = 3;
foreach (taskClass task in test)
{
int column = 1;
if (task != null)
{
sheet.Cells["A3:D3"].Copy(sheet.Cells[String.Format("A{0}:D{0}", row)]);
sheet.Cells[row, column++].Value = String.Format("{0} {1}", task.employeeDetails.FirstName, task.employeeDetails.Surname);
sheet.Cells[row, column++].Value = task.Subject;
sheet.Cells[row++, column].Value = task.Description;
}
}
Byte[] arr = excel.GetAsByteArray();
File.WriteAllBytes(tempFileName, arr);
}
}
}
}
}
catch (Exception ex)
{
throw new Exception(String.Format("Message:{1}{0}{0}StackTrace:{2}", Environment.NewLine, ex.Message, ex.StackTrace));
}
}
else
throw new Exception(String.Format("Could not access template: {0}", xlsTemplate));
return workUnit != null ? tempFileName : webTempFileName;
}
}
}
关注点
对于工作表数据,我使用了一个巧妙的工具,这样我就可以将整个模板留空,只在模板中留下标题和第一行。现在我可以格式化包含所有数据的整行,并且所有内容都只会按需复制到下一行。因此,我的工作表的大小只够用,不多不少。因此,无需在数据完成后进行长时间的格式化,这看起来很不专业:-)。
我使用的工具是VS中内置的Excel工具。(在示例中)
sheet.Cells["A3:D3"].Copy(sheet.Cells[String.Format("A{0}:D{0}", row)]);
历史
这是我的第一篇文章,由于几乎没有帮助(谷歌或其他),我认为将我目前在这方面的成功发布出来是一个好主意,以便那些可能遇到相同问题和困境的人。