65.9K
CodeProject 正在变化。 阅读更多。
Home

在 DevExpress XAF 中创建 Excel 报表

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (3投票s)

2014 年 1 月 15 日

CPOL

3分钟阅读

viewsIcon

28053

在 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)]);

历史

这是我的第一篇文章,由于几乎没有帮助(谷歌或其他),我认为将我目前在这方面的成功发布出来是一个好主意,以便那些可能遇到相同问题和困境的人。

© . All rights reserved.