信息系统开发平台OpenExpressApp - 报表模块支持ReportObjectView

  在信息系统开发平台OpenExpressApp - 框架待完善工作事项中提到要支持报表模块,由于项目组这期任务需要报表功能,于是这几天把这个功能加进来了。因为没有时间重新设计开发一个C#版的报表引擎,所以现在所实现的报表模块是基于在.Net下如何跨语言调用Delphi写的报表引擎中介绍过的我几年前写的一个delphi下的报表引擎。

  本篇介绍一下在OpenExpressApp下的报表模块实现以及使用。

使用ReportModule

  • 之前的查询窗体的工程属性UI:列表视图

下面为框架以前对查询窗体QueryObject的支持,如下面代码所示生成下图:

[DefaultObject("3AEF18F3-50F3-4120-A0AB-0330A74FB084", Catalog = "指标管理",
ModuleType
= ModuleType.Query, Index = 600), Label("技术经济指标模块")]
[NavigateQueryType(
typeof(ProjectIndicatorNavigateCriteria), Header = "选择项目PBS")]
[QueryObject(
typeof(ProjectPBSProperty))] //工程属性
[QueryObject(typeof(ProjectCostIndicator))]
[
...]
public class ProjectIndicatorQueryObject: BaseQueryObject { }

  • 报表视图,数据来源与业务对象

为了与查询窗体集成,编写代码方式与之前类似,如果想让【工程属性】显示为报表样式,UI如下所示,则需要更改代码使用ReportObject:

[DefaultObject("3AEF18F3-50F3-4120-A0AB-0330A74FB084", Catalog = "指标管理",
ModuleType
= ModuleType.Query, Index = 600), Label("技术经济指标模块")]
[NavigateQueryType(
typeof(ProjectIndicatorNavigateCriteria), Header = "选择项目PBS")]
[NotAllowEdit, NotAllowNew, NotAllowRemove]
[QueryObject(
typeof(ProjectPBSProperty))]
[QueryObject(
typeof(ProjectPBSPropertyReportObject))]//工程属性
[......]
public class ProjectIndicatorQueryObject: BaseQueryObject { }

//定义包括的业务对象,如果报表包含多个业务对象,可以通过多个ReportObject来指定业务对象
[ReportObject(
typeof(ProjectPBSProperty))]
[DefaultObject(
"B9C1AB3C-CF1E-4f29-985A-9758BF125CAD", ShowInModule = false, Index = 700), Label("工程属性报表")]
[NotAllowEdit, NotAllowNew, NotAllowRemove]
public class ProjectPBSPropertyReportObject : ReportObject { }

OpenModule之ReportModule  

  • 总体目标
  1. OpenExpressApp是一个基于对象的应用框架,所以需要考虑如何如何通过对象的方式来实现报表功能
  2. 对于数据来源,基于业务对象是一种方式,而以前一直使用记录Record来作为报表数据源,这个也需要提供支持
  3. 实现是需要重用框架的View和类库的概念,与OpenExpressApp框架进行较好的集成
  4. 考虑到报表模块不是框架必须的,并且现在报表模块实现中使用到的报表引擎不是开源产品,所以需要考虑在框架实现中不能影响现在框架的应用,所以报表模块将作为OpenModule中的一个模块来发布,而不是内置在OpenExpressApp框架内部。  

基于以上一些目标,现在已经实现了报表模块,下面我将对实现方案进行简要描述。(注:读者需要对OpenExpressApp的查询业务对象部分有所了解。

  • Solution结构以及主要类库介绍

新增加了一个OpenModule目录,同之前示例代码一样,模块的编写一般会有一个类库,一个是与界面相关的项目,ReportModule同样需要这两个项目:

  1. OpenExpressApp.ReportModule.Library:报表模块相关类库,如ReportObject
  2. OpenExpressApp.ReportModule.WPF:报表模块UI相关,如ReportObjectView

OpenExpressApp.ReportModule.Library

  • ReportObject:报表业务对象
    所有报表业务对象都需要从ReportObject继承下来,如下面的示例代码片段:
public class ProjectPBSPropertyReportObject : ReportObject { }


  • ReportObjectAttribute:报表对象的数据来源属性标签,为了便于定义业务对象数据来源,提供类库属性定义
    //数据来源业务对象,约定通过业务对象的GetList方法获取数据
    [ReportObject(
    typeof
    (ProjectPBSProperty))]
     public class ProjectPBSPropertyReportObject : ReportObject { }
  • 重用查询业务对象QueryObject,使用ReportObject对象
    [QueryObject(typeof(ProjectPBSProperty))]
    [QueryObject(
    typeof(ProjectPBSPropertyReportObject))]//工程属性
    public class ProjectIndicatorQueryObject: BaseQueryObject    {    }
    [......]
  • OpenJsonObject:从SQL获取对象数据,并生成Json格式数据串
    由于需要与Delphi的报表引擎交互,而以前的报表引擎是基于数据集的,所以业务对象的数据进入报表时采纳了json串来进行交互。而支持SQL获取数据,也需要进行交互,所以也采纳了Json进行交互,格式定义如下:
            Name:表名称    
            Schemas: {fld1:X, fld2:X},  //X为GSPDataType
            Records=[{fld1:XX,fld2:xxx,fld3:xxx}, {fld1:XX,fld2:xxx,fld3:xxx}]
            fdtString = 0;  fdtBoolean = 1;  fdtDouble = 2;  fdtInt = 3;  fdtDateTime = 4;
  • ReportDataStore:报表数据,支持业务对象数据源和SQL获取数据源

OpenExpressApp.ReportModule.WPF

  • ReportObjectView:从WPFObjectView继承(理解核心元素ObjectView) ,生成ReportFram报表控件,Data绑定ReportDataStore,模块内部支持报表格式设计并自动保存(设计功能后期将作为一个Command实现,这样可以进行功能权限设定)
代码
/*******************************************************
*
* 作者:周金根
* 创建时间:20100408
* 说明:文件描述
* 版本号:1.0.0
* 报表View,指定设计样式MetaData和数据源ReportDataStore后可以Open报表
* 历史记录:
* 创建文件 周金根 20100408
*
******************************************************
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AxReportFram;
using System.Windows.Forms.Integration;
using System.Reflection;
using OpenExpressApp.ReportModule.Library;
using System.Collections;
using System.Windows.Forms;
using OpenExpressApp.Module.WPF;
using System.Diagnostics;

namespace OpenExpressApp.ReportModule.WPF
{
public class ReportObjectView : WPFObjectView
{
public ReportObjectView(Type type) : base(type)
{
this._metaDataId = ApplicationModel.GetBusinessObjectInfo(type).Id;;
}
internal Guid _metaDataId;
internal ReportObjectMetaData ReportObjectMetaData { get; set; }


private ReportObjectMetaData roMetaData;

public ReportObjectView() : base(typeof(ReportObject)) { }

private ReportDataStore _reportDataStore;

public new ReportDataStore Data
{
get { return _reportDataStore; }
set
{
Debug.Assert(value
is ReportDataStore, "ReportObjectView.Data必须是ReportDataStore");
_reportDataStore
= value;
ClearDataSource();
BuildData();
}
}

private void BuildData()
{
foreach (DataSourceInfo item in _reportDataStore.Datasources)
{
string jsonCustomers = BuildCustomersJson(item.Objects, item.Type);
AddDataSource(jsonCustomers);
}
foreach (OpenJsonObject item in _reportDataStore.JsonDatas)
{
AddDataSource(item.JsonData);
}
}

public override void RefreshCurrentObject() {}

public override object CurrentObject
{
get
{
return null; //报表没有当前行
}
set {}
}

private AxReportFramX _reportFram;

protected override object CreateControl()
{
WindowsFormsHost reportHost
= new WindowsFormsHost();
_reportFram
= new AxReportFram.AxReportFramX();

_reportFram.OnSaveMetaData
+= new IReportFramXEvents_OnSaveMetaDataEventHandler(_reportFram_OnSaveMetaData);
reportHost.Child
= _reportFram;
return reportHost;
}

protected virtual void OnMetaDataChanged()
{
//保存MetaData到OpenExpressApp数据库
MetaData = _reportFram.XML;

if (this.MetaDataChanged != null)
{
this.MetaDataChanged(this, EventArgs.Empty);
}
}

public event EventHandler MetaDataChanged;

void _reportFram_OnSaveMetaData(object sender, IReportFramXEvents_OnSaveMetaDataEvent e)
{
OnMetaDataChanged();
}

#region 根据对象类别生成Json字符串

// 设计:转换json字符串到GSPTable 2010.03.22
// 形式如: Name:表名称
// Schemas: {fld1:X, fld2:X}, //X为GSPDataType
// Records=[{fld1:XX,fld2:xxx,fld3:xxx}, {fld1:XX,fld2:xxx,fld3:xxx}]
// fdtString = 0; fdtBoolean = 1; fdtDouble = 2; fdtInt = 3; fdtDateTime = 4;
private string BuildCustomersJson(IList list, Type type)
{
StringBuilder sbJson
= new StringBuilder("");
//开始
sbJson.Append("{");
//添加表名
sbJson.Append(String.Format(@"Name:""{0}"",", type.Name));
//添加字段Schema
sbJson.Append("Schemas: {");
PropertyInfo[] propInfos
= type.GetProperties();
foreach (PropertyInfo propInfo in propInfos)
{
sbJson.Append(String.Format(
"{0}:{1},", propInfo.Name, PropertyTypeToDataType(propInfo)));
}
sbJson.Append(
"},");
//添加记录
sbJson.Append("Records: [");
foreach (var item in list)
{
sbJson.Append(
"{");
foreach (PropertyInfo propInfo in propInfos)
{
object value = propInfo.GetValue(item, null);
if (value == null) continue;
string strValue = "";
if ((typeof(String) == propInfo.PropertyType) || (typeof(Guid) == propInfo.PropertyType))
strValue
= "\"" + value.ToString() + "\"";
else if (typeof(Boolean) == propInfo.PropertyType)
strValue
= Convert.ToInt16(value).ToString();
else
strValue
= value.ToString();

sbJson.Append(String.Format(
"{0}:{1},", propInfo.Name, strValue));
}
sbJson.Append(
"},");
}
sbJson.Append(
"]");
//末尾
sbJson.Append("}");
return sbJson.ToString();
}

// fdtString = 0; fdtBoolean = 1; fdtDouble = 2; fdtInt = 3; fdtDateTime = 4;
private int PropertyTypeToDataType(PropertyInfo propInfo)
{
if (typeof(String) == propInfo.PropertyType) return 0;
else if (typeof(Boolean) == propInfo.PropertyType) return 1;
else if (typeof(Double) == propInfo.PropertyType) return 2;
else if (typeof(int) == propInfo.PropertyType) return 3;
else if (typeof(DateTime) == propInfo.PropertyType) return 4;
else return 0;
}

#endregion

#region 封装报表控件

public string MetaData
{
get
{
return ReportObjectMetaData.MetaData;
}
set
{
ReportObjectMetaData.MetaData
= value;
ReportObjectMetaData.Save();
}
}

public void AddDataSource(string json)
{
_reportFram.AddDataSource(json);
}

public void OpenReport()
{
if (!String.IsNullOrEmpty(MetaData)) _reportFram.XML = MetaData;
_reportFram.OpenReport(
new Guid(), false);
}

public void ClearDataSource()
{
_reportFram.ClearDataSource();
}

#endregion
}
}

  ReportObjectMetaData是一个内置的保存报表视图设计格式的一个业务对象,在数据库OpenExpressApp中对应表ReportObjectMetaData,其中自动Id为ReportObject业务对象的对象Id,MetaData为报表设计样式的XML格式字符串。

  • 与OpenExpressApp的QueryForm模板窗口集成

  在QueryFormController.cs中根据QueryObjectAttribute来生成相应的Tab页签,通过以下代码红色部分内容,调用业务对象类型默认生成的视图生成器来生成ReportObjectView

private void CreateTabItem(QueryObjectAttribute queryObjInfo)
{
Type type
= queryObjInfo.ObjectType;
//生成View和Controller
WPFObjectView view = null;
if (ViewType.DetailView == queryObjInfo.ViewType)
{
//生成DetailView
  }
else
{
//根据对象类型自动生成View
view = DefaultViewCreator.Create(type);
if (view == null)
{
//生成ListView
  }
}

DefaultViewCreator是OpenExpressApp框架内部的一个全局注册类,通过 Register方法可以注册特定离诶性能过的业务对象视图生成器

代码

namespace OpenExpressApp.Module.WPF
{
public static class DefaultViewCreator
{
/// <summary>
/// 第一个参数Type:业务对象类型
/// 第二个参数Type:注册生成WPFObjectView类型
/// </summary>
private static Dictionary<Type, ICreateDefaultView> _creatorMap = new Dictionary<Type, ICreateDefaultView>();

public static void Register(Type boType, ICreateDefaultView creatorType)
{
_creatorMap.Add(boType, creatorType);
}

public static WPFObjectView Create(Type type)
{
foreach (var item in _creatorMap)
{
if (item.Key.IsAssignableFrom(type))
return item.Value.CreateView(type);
}
return null;
}
}

public interface ICreateDefaultView
{
WPFObjectView CreateView(Type boType);
}
}

在ReportModule模块装载代码中,加入注册ReportObject与ReportObjectView的生成对应

代码
public class ReportWPFModule : AdaptCommandModule
{
public override void Initialize()
{
base.Initialize();
DefaultViewCreator.Register(
typeof(ReportObject), new CreateDefaultReportView());
}
}
代码
internal class CreateDefaultReportView : ICreateDefaultView
{
#region ICreateDefaultView Members

public WPFObjectView CreateView(Type boType)
{
//生成DetailView
var view = new ReportObjectView(boType);
view.DataLoader
= new ReportObjectViewController(view);
return view;
}

#endregion
}

其中用到了ReportObjectViewController,这是一个获取数据打开报表的一个视图控制类,与OpenExpressApp的ViewController功能类似

代码
namespace OpenExpressApp.ReportModule.WPF
{
internal class ReportObjectViewController : ViewDataLoaderBase, IControlWrapper
{
public ReportObjectViewController(ReportObjectView view)
:
base(view) { }

public new ReportObjectView View
{
get
{
return base.View as ReportObjectView;
}
}

protected override string FactoryMethod
{
get { return "GetList"; }
}

private Type GetQueryType(Type entityType)
{
var assembly
= entityType.Assembly;
var typeName
= entityType.FullName;
return assembly.GetType(typeName + "List") ??
assembly.GetType(typeName
+ "s");
}


public override void AsyncGetObject(string getListMethod, params Object[] getListParam)
{
ReportDataStore rds
= new ReportDataStore();
ReportObject ro
= Activator.CreateInstance(View.BOType) as ReportObject;
//添加业务对象数据源
foreach(var bo in ro.BusinessObjects)
{
using (this._dataProvider.DeferRefresh())
{
this._dataProvider.IsAsynchronous = false;
this._dataProvider.ObjectType = this.GetQueryType(bo);
this._dataProvider.FactoryMethod = getListMethod;
this._dataProvider.FactoryParameters.Clear();
foreach (var item in getListParam)
{
this._dataProvider.FactoryParameters.Add(item);
}
}
rds.AddDataSource(_dataProvider.Data
as IList, bo);
}
//添加业Sql数据源(现在为分批获取,以后改为打包获取数据,减少网络交互次数)
foreach (var bo in ro.SqlObjects)
{
string sql = bo.Value;
//todo:替换参数,根据过滤条件生成最终Sql
rds.AddJsonData(OpenJsonObject.GetBySql(sql, bo.Key, null));
}

//延迟获取元数据,在这里装载MetaData
if (View.ReportObjectMetaData == null)
{
if (ReportObjectMetaData.Exists(View._metaDataId))
{
View.ReportObjectMetaData
= ReportObjectMetaData.Get(View._metaDataId);
}
else
{
View.ReportObjectMetaData
= ReportObjectMetaData.New();
View.ReportObjectMetaData.Id
= View._metaDataId;
}
}
if (!(View.Control as FrameworkElement).IsLoaded)
(View.Control
as FrameworkElement).Loaded += delegate(object sender, RoutedEventArgs e)
{
View.Data
= rds;
View.OpenReport();
};
else
{
View.Data
= rds;
View.OpenReport();
}
}

#region IControlWrapper Members

public object Control
{
get { return View.Control; }
}

#endregion

protected override Type FindQueryType()
{
throw new NotImplementedException();
}
}
}

 

更多内容: 开源信息系统开发平台之OpenExpressApp框架.pdf

 

欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]

posted on 2010-05-17 14:25  周 金根  阅读(2962)  评论(5编辑  收藏  举报

导航