不是指ActiveReports Viewer 本身的本地化哦!指报表数据的本地化.
用过 ActiveReports for .Net (以下简称AR)的都知道它的优势(可代码化),这样就提供了很多报表功能开发的空间给程序员。
不多说了,下面将通过我的一个EPR软件来介绍一下实现 AR 2.0 的报表本地化功能(主要通过UI贴图+讲解方式):
开发环境:VS2005 .Net 2.0 + C# + SQL2005 + AR 2.0
=========================================================
讲解一下如何实现这几种本地化语言:
1,报表预定义(快速!) [Default]
就是指实例化报表时不应用本地化设置;
2,CIMS本地化设置(推荐!)(System)
随主系统的本地化设置一样;
3,简体中文(Chinese Simplified)
4,繁体中文(Chinese Traditional)
ReportLocal 类 定义一个属性:
{
get { return m_canActive;}
set { m_calActive = value;}
}
这个属性在ReportLocal类实例化代入,来自主系统用户权限控制,如果你的程序没有相关权限控制可忽略。
这个Active属性用来表示将要使用的本地化语言:
/// 获取当前报表本地化设置('CIMS本地化设置' 除外)
/// </summary>
public ReportLocalizableEnum Active
{
get
{
if (CanActive == false ) return ReportLocalizableEnum.Default;
if (m_active == ReportLocalizableEnum.System) m_active = GetReportLocalizableWithSystem;
return m_active; // 来自存储参数
}
}
这些属性,函数很关键,主要收集可以本地化的报表组件(未定义DataField属性的):
/// 要本地化的报表控件集合,用于OnInitControlsCollection()函数。
/// 参数:Key = Control.Name
/// 参数:Value = 控件(Label;TextBox,CheckBox...)
/// </summary>
public Hashtable Collection { get { return rptControls; } }
public Hashtable CreateReportControls(DataDynamics.ActiveReports.ActiveReport rpt, string sectionNameOrType)
{
if (rpt == null ) return null ;
Hashtable ctrls = new Hashtable();
Section rs = null ;
bool all = (sectionNameOrType == UnknownSection || string .IsNullOrEmpty(sectionNameOrType));
// 所有报表区域
if (all == false ) {
try
SectionType type = (SectionType)System.Enum.Parse( typeof (SectionType), sectionNameOrType, true );
foreach ( rs in rpt.Sections)
{
if (rs.Name == sectionNameOrType) break;
if (type == rs.Type) break;
}
}
catch {
all = true;
break;
}
}
if (all || (rs == null)) {
foreach ( rs in rpt.Sections)
AddControlsInfo(rs, ctrls);
}
}
else {AddControlsInfo(rs, ctrls); }
return ctrls;
}
private void AddControlsInfo(Section section, Hashtable collections)
{
SectionType type = section.Type;
ARControl ctrl;
foreach ( ctrl in section.Controls) {
if ( string .IsNullOrEmpty(ctrl.DataField) == false ) continue ;
// 只收集没有是定义DataField属性的组件!
ReportControlInfo ctrlInfo = null ;
if (ctrl is Label)
{
Label lab = (Label)ctrl;
ctrlInfo = new ReportControlInfo(lab.Text);
}
else if (ctrl is TextBox) {
TextBox txt = (TextBox)ctrl;
if ( string .IsNullOrEmpty(txt.Text)) continue ;
ctrlInfo = new ReportControlInfo(txt.Text); // TextBox类型必须是设置了Text的!
}
else if (ctrl is CheckBox) {
CheckBox chk = (CheckBox)ctrl;
if ( string .IsNullOrEmpty(chk.Text)) continue ;
ctrlInfo = new ReportControlInfo(chk.Text); //CheckBox类型必须是设置了Text的!
}
if ((ctrlInfo != null ))
ctrlInfo.DataField = this .GetDataField(ctrlInfo.Text, string .Empty);
ctrlInfo.Section = type;
ctrlInfo.Parent = ctrl.Parent;
collections.Add(ctrl.Name, ctrlInfo);
}
}
}
/// <summary>
/// 根据组件的Name返回关联本地化设置的‘字段名’,仅判断一些常用的。
/// </summary>
/// <param name="controlText"> 组件.Text </param>
/// <param name="defaultField"> 默认使用的‘字段名’,一般为空,需要后续修正 </param>
protected string GetDataField( string controlText, string defaultField)
{
if (controlText.IndexOf( " ted By " ) > - 1 ) {
return DBColumns.CreatedBy;
}
else if (controlText.IndexOf( " red By " ) > - 1 ) {
return DBColumns.PreparedBy;
....... // 根据你的报表来添加判断,主要是用于后面的BatchCreate功能,避免手功添加
}
这是ReportControlInfo类(用于收录组件的相关参数):
... {
protected string m_text;
protected string m_dataField;
protected object m_section;
protected object m_parent;
public ReportControlInfo(string text)
...{
m_text = text;
}
public ReportControlInfo(string text, object section)
...{
m_text = text;
m_section = section;
}
public ReportControlInfo(string text, string dataField)
...{
m_text = text;
m_dataField = dataField;
}
/**//// <summary>
/// 获取或设置 组件预定义了的文本
/// </summary>
public string Text ...{
get ...{ return m_text; }
set ...{ m_text = value; }
}
/**//// <summary>
/// 获取或设置 用于关联本地化设置的‘字段名’
/// </summary>
public string DataField ...{
get ...{ return m_dataField; }
set ...{ m_dataField = value; }
}
/**//// <summary>
/// 获取或设置 组件的父容器(PageHeader,GroupHeader...),一般可不设置
/// </summary>
public object Parent ...{
get ...{ return m_parent; }
set ...{ m_parent = value; }
}
/**//// <summary>
/// 获取或设置 组件所在的节(Report.Section),可以是SectionName OR SectionType
/// </summary>
public object Section ...{
get ...{ return m_section; }
set ...{ m_section = value; }
}
}
=================================
下面将是实现“报表本地化关联”功能的核心:
为于实现报表同主系统使用一致的本地化定义,下面部分是必须的!
系统的本地化资源编辑器(由于此ERP的应用关系,编辑器中增加了对应用人的权限控制),源代码不提供:
这是 关联系统本地化设置 的报表本地化配置器:
解释一下各字段作用:
所属区域:指要本地化的组件所在的Section,如果为空,则为公共设置,任何报表都会搜索这些关联设置;
字段名:一般为数据表的ColumnName,要同“本地化编辑器”里的DataField定义一致;
组件名:将本地化的AR.Control,应于实际报表设计时一致,如果报表做了更改,可使用“批量修复”功能来自动修复refContrlName;
默认文本:AR.Control 预定义的Text,如果用户想始终使用这个DefaultText,只需要定义后钩选“仅使用默认标题”;
备注:不用解释了。
“报表效果预览”只是应用了最上面的参数设置。
下面,录制了一个“批量创建”报表组件的本地化关联设置的动画(文件太大!只能用GIF格式上传了,UI有失真):
索引关联设置类:
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace CIMS.Report
... {
public class ReportsLocalization : IDisposable
...{
Const#region Const
public static string[] Sections
...{
get
...{
return new string[] ...{
"ReportHeader",
"PageHeader",
"GroupHeader",
"Detail",
"ReportFooter",
"PageFooter",
"GroupFooter",
"Unknown!"};
}
}
public const string COL_DATAFIELD = "字段名";
public const string COL_REPORTID = "报表编码";
public const string COL_SECTIONNAME = "所属区域";
public const string COL_CONTROLNAME = "组件名";
public const string COL_CONTROLTEXT = "默认文本";
public const string DATATABLE_NAME = "ReportsLocalization";
#endregion
Variables#region Variables
protected string m_reportID = string.Empty;
protected string m_activeDBForm = string.Empty;
protected Document.ReportLocal reportLocal = null;
protected EnToolTips refLocal = null;
protected DataView dv = null;
#endregion
Init#region Init
/**//// <summary>
/// 实例化本地化报表关联设置加载类
/// </summary>
/// <param name="reportID">报表枚举成员(ReportsAssembly.MemberID)</param>
public ReportsLocalization(string reportID):this(reportID,string.Empty) ...{ }
/**//// <summary>
/// 实例化本地化报表关联设置加载类
/// </summary>
/// <param name="reportID">报表枚举成员(ReportsAssembly.MemberID)</param>
/// <param name="dbFormName">将应用自定义本地化报表的数据模块(如果为空,则表示将应用到所有模块)</param>
public ReportsLocalization(string reportID, string dbFormName)
...{
if (string.IsNullOrEmpty(reportID)) ...{ throw new NullReferenceException("报表成员未指定!"); }
m_reportID = reportID;
m_activeDBForm = dbFormName;
this.OnInit();
}
protected void OnInit()
...{
if (Common.Common.CIMSReadOnly) return;
reportLocal = new CIMS.Document.ReportLocal(ActiveDBForm);
refLocal = new EnToolTips(ActiveDBForm);
this.FillData();
}
protected void FillData()
...{
string paramReportID = "@ReportID";
SqlDataAdapter sda = new SqlDataAdapter("SELECT ReportID AS " + COL_REPORTID + ", DataField AS " + COL_DATAFIELD + ", SectionName AS " + COL_SECTIONNAME + ", ControlName AS " + COL_CONTROLNAME + ", ControlText AS " + COL_CONTROLTEXT + ", Lock AS " + Utils.LOCK + " FROM " + DATATABLE_NAME + " Where ReportID Is Null Or ReportID = " + paramReportID + " Order By ReportID", Common.SqlHelper.ConnectionBase.CONCIMS);
sda.SelectCommand.Parameters.Add(paramReportID, SqlDbType.VarChar, 50).Value = this.ReportID;
DataTable dt = new DataTable(DATATABLE_NAME);
using (sda)
...{
dt.BeginLoadData();
sda.Fill(dt);
dt.EndLoadData();
}
dv = dt.DefaultView;
}
#endregion
Properties#region Properties
/**//// <summary>
/// 获取 本地化定位器
/// </summary>
public EnToolTips Localizer ...{ get ...{ return refLocal; } }
/**//// <summary>
/// 获取 报表枚举成员(ReportsAssembly.MemberID)
/// </summary>
public string ReportID ...{get ...{ return m_reportID; }}
/**//// <summary>
/// 获取将应用报表本地化关联设置的数据模块(如果为空,则表示将搜索所有模块的本地化设置资源)
/// </summary>
public string ActiveDBForm ...{ get ...{ return m_activeDBForm; } }
#endregion
Localizable#region Localizable
/**//// <summary>
/// 获取报表组件(按Control.Name)的本地化关联设置。不存在设置就返回空:String.Empty
/// </summary>
/// <param name="ctrlName">组件名</param>
/// <returns>不存在设置就返回空:String.Empty</returns>
public string GetLocalText(string ctrlName)
...{
string nameFilter = COL_CONTROLNAME + "='" + ctrlName + "'";
dv.RowFilter = nameFilter;
if (dv.Count == 0) return string.Empty;
dv.RowFilter += " And " + COL_REPORTID + "='" + ReportID + "'";//报表成员优先
if (dv.Count > 0) return GetActiveLocalText(dv[0]);
dv.RowFilter = nameFilter + " And " + COL_REPORTID + " IS Null";//搜索公共成员
if (dv.Count > 0) return GetActiveLocalText(dv[0]);
return string.Empty;
}
protected string GetActiveLocalText(DataRowView drv)
...{
bool useDefaultText = Convert.ToBoolean(Common.Common.IsNull(drv[Utils.LOCK], false));
if (useDefaultText) return Common.Common.IsNull(drv[COL_CONTROLTEXT],drv[COL_DATAFIELD]).ToString();//使用默认文本
string dataField = Common.Common.IsNull(drv[COL_DATAFIELD], string.Empty).ToString();
switch (reportLocal.Active)
...{
case ReportLocalizableBase.ReportLocalizableEnum.ChineseSimplified:
return refLocal[dataField];
case ReportLocalizableBase.ReportLocalizableEnum.ChineseTraditional:
return refLocal.ConvertZNString(refLocal[dataField]);
case ReportLocalizableBase.ReportLocalizableEnum.English:
return refLocal.IsExist(dataField) ? refLocal.EnToolTip : string.Empty;
default:
return string.Empty;
}
}
/**////// <summary>
///// 获取报表组件(按Control.Text)的本地化关联设置
///// </summary>
///// <param name="ctrlText">组件的预设文本</param>
///// <returns></returns>
//public string GetLocalText(string ctrlText)
//{
// dv.RowFilter = COL_CONTROLNAME + "='" + ctrlName + "'";
//}
//protected bool SetRowFilter(string filter, bool ctrlNameFilter)
//{
// dv.RowFilter = (ctrlNameFilter ? COL_CONTROLNAME : COL_CONTROLNAME) + "='" + filter + "'";
//}
#endregion
IDisposable#region IDisposable
public void Dispose()
...{
if (Localizer != null) Localizer.Dispose();
if (dv != null) dv.Dispose();
}
#endregion
}
}
通过上面一些定义,就可以在实例化报表时实现本地化报表了!
先收集本地化组件到Collection,然后遍历集合并:
rptTest rpt = new rptTest(...);
ReportLocal reportLocal = new ReportLocal( true );
reportLocal.OnInitControlsCollection();
IDictionaryEnumerator ie = collection.GetEnumerator();
while (ie.MoveNext())
{
string controlName = ie.Key.ToString();
string localText = reportLocal.GetLocalText(controlName); // 得到本地化资源
if ( string .IsNullOrEmpty(localText)) continue ;
ARControl ctrl = (ARControl)((ReportControl)ie.Value).Control;
if ( ctrl is Label)
((Label)ctrl).Text = localText ; // 设置Text属性完成本地化
else if ......
}
最终报表本地化预览:
繁体报表结果预览:
英文报表本地化结果(BUG: SubReport 还不能本地化):