博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
信息系统开发平台OpenExpressApp - 报表模块支持ReportObjectView
阅读量:5307 次
发布时间:2019-06-14

本文共 12133 字,大约阅读时间需要 40 分钟。

  在中提到要支持报表模块,由于项目组这期任务需要报表功能,于是这几天把这个功能加进来了。因为没有时间重新设计开发一个C#版的报表引擎,所以现在所实现的报表模块是基于在中介绍过的我几年前写的一个delphi下的报表引擎。

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

使用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 { }

2010051711132635.jpg

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

为了与查询窗体集成,编写代码方式与之前类似,如果想让【工程属性】显示为报表样式,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 { }

2010051711202994.jpg

OpenModule之ReportModule  

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

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

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

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

2010051711000510.jpg

  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继承() ,生成ReportFram报表控件,Data绑定ReportDataStore,模块内部支持报表格式设计并自动保存(设计功能后期将作为一个Command实现,这样可以进行功能权限设定)
ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
/*
******************************************************
*
* 作者:周金根
* 创建时间: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方法可以注册特定离诶性能过的业务对象视图生成器

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
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的生成对应

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
public
class
ReportWPFModule : AdaptCommandModule
{
public
override
void
Initialize()
{
base
.Initialize();
DefaultViewCreator.Register(
typeof(ReportObject), new
CreateDefaultReportView());
}
}
ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
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功能类似

ContractedBlock.gif
ExpandedBlockStart.gif
代码
 
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();
}
}
}

 

更多内容:productView-pdf_46x35.gif

 

欢迎转载,转载请注明:转载自 [ ]

转载于:https://www.cnblogs.com/zhoujg/archive/2010/05/17/1737146.html

你可能感兴趣的文章
laravel的速查表
查看>>
5-24
查看>>
Javascript一些小细节
查看>>
Struct
查看>>
在WPF程序中使用摄像头兼谈如何使用AForge.NET控件(转)
查看>>
Linux修改用户shell
查看>>
[译]我是怎么构建Node.js程序的
查看>>
suse 源的添加与删除,以及源地址
查看>>
56个 PHP 开发常用代码片段(上)
查看>>
maven安装与项目移植
查看>>
大数据告诉你互联网到底有多大?完全超出你想象!
查看>>
C语言输入日期计算是该年的第几天
查看>>
Caliburn v2 变更-模块化
查看>>
Python之路,Day3 - Python基础3
查看>>
实验 4 在分支循环结构中调用自定义函数
查看>>
Java学习笔记-3.类与对象
查看>>
力扣——车的可用捕货量
查看>>
Redis参数
查看>>
当多个客户请求一个servlet时,引擎为每个客户启动一个线程,那么servlet类的成员变量被所有的线程共享?...
查看>>
jquery更改输入框type为密码框password
查看>>