信息系统开发平台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功能类似

 

 

 

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

 

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

 

代码
 
  
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();
}
}
}







本文转自 jingen_zhou 51CTO博客,原文链接: http://blog.51cto.com/zhoujg/518560 ,如需转载请自行联系原作者

相关文章
|
2月前
|
人工智能 监控 数据可视化
智慧工地管理平台可视化源码
“智慧工地”将施工企业现场视频管理、建筑起重机械安全监控、现场从业人员管理、物料管理、进度管理、扬尘噪声监测等现场设备有机、高效、科学、规范的结合起来,真正实现工程项目业务流与现场各类监控源数据流的有效结合与深度配合,实现了建筑企业内部对各工程项目的集约式管理模式,大大提高了施工企业的工作效率和管理力度,彻底颠覆了原有的传统施工现场管理模式,使施工企业的竞争力得到了质的跨越。
41 0
|
19天前
|
数据采集 数据可视化 算法
深入解析ERP系统的业务智能与报表分析模块
深入解析ERP系统的业务智能与报表分析模块
21 3
|
22天前
|
数据挖掘
深入解析ERP系统的人力资源管理模块
深入解析ERP系统的人力资源管理模块
23 1
|
6月前
|
运维 监控 安全
易云维通过IBMS系统为医院搭建统一的监控管理平台
易云维医院楼宇智能化管理系统(IBMS系统)可以通过调研医院项目现场情况,了解用户的实际需求,为用户提供合理投资、高效、舒适、方便的环境空间;对医院建筑多个弱电子系统进行集中监控,确保各个弱电子系统安全、高效、稳定运行。
115 1
|
7月前
|
存储 容灾 数据管理
商业智能系统具有的主要功能
商业智能系统具有的主要功能
109 0
|
12月前
|
小程序 BI
智慧校园学生平台综合评价子系统源码,报表自动生成
学生评价系统是智慧校园电子班牌系统中的其中一个子系统,各学科教师通过小程序或是班级互动电子屏直接评价,以积分的形式每日即时评价学生的按时到校、作业完成、课堂表现等情况,形成学生学习兴趣和学习习惯数据。
|
存储 数据可视化 前端开发
集成平台下连接器设计规范: 调研-实践-思考
云集成是当今企业面临的主要挑战之一,为了满足对安全可靠的云集成解决方案日益增长的需求,一些供应商已开始提供集成服务,称为集成平台即服务 (iPaaS)。对于集成平台而言, 连接器可以看做平台运行时引擎的可重用扩展, 能够将 集成平台 应用程序与第三方 API、数据库和标准集成协议集成。连接器抽象了连接到目标系统所涉及的技术细节。本文探索了部分优秀的集成平台以及其各自的连接器规范, 对于现有链接场景的存在问题进行了一定的思考, 同时提出了我们自己的数据集成平台以及连接器设计思路, 希望可以通过Full-Code, Low-Code以及Platform三种方案灵活切换的方式解决实际需求。
933 0
从“信息集成整合”来分析OA系统的技术体系
在整个组织管理过程中,我们通过OA软件系统来完成这部分的支撑工作,那么对于OA系统软件又是如何达到的呢,这里我们重点阐述两方面的技术支撑体系,一方面是其中流程血脉论为基础的工作流引擎,二是能打通与其他业务系统的信息集成整合。
1026 0