在信息系统开发平台OpenExpressApp - 框架待完善工作事项中提到要支持报表模块,由于项目组这期任务需要报表功能,于是这几天把这个功能加进来了。因为没有时间重新设计开发一个C#版的报表引擎,所以现在所实现的报表模块是基于在.Net下如何跨语言调用Delphi写的报表引擎中介绍过的我几年前写的一个delphi下的报表引擎。
本篇介绍一下在OpenExpressApp下的报表模块实现以及使用。
下面为框架以前对查询窗体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 { }
基于以上一些目标,现在已经实现了报表模块,下面我将对实现方案进行简要描述。(注:读者需要对OpenExpressApp的查询业务对象部分有所了解。)
新增加了一个OpenModule目录,同之前示例代码一样,模块的编写一般会有一个类库,一个是与界面相关的项目,ReportModule同样需要这两个项目:
public class ProjectPBSPropertyReportObject : ReportObject { }
//数据来源业务对象,约定通过业务对象的GetList方法获取数据
[ReportObject(typeof (ProjectPBSProperty))]
public class ProjectPBSPropertyReportObject : ReportObject { }
[QueryObject( typeof (ProjectPBSProperty))]
[QueryObject( typeof (ProjectPBSPropertyReportObject))] // 工程属性
public class ProjectIndicatorQueryObject: BaseQueryObject { }
[......]
/* ******************************************************
*
* 作者:周金根
* 创建时间: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格式字符串。
在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();
}
}
}