不是指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 类 定义一个属性:
public bool CanActive
{
get{ return m_canActive;}
set{ m_calActive = value;}
}
这个属性在ReportLocal类实例化代入,来自主系统用户权限控制,如果你的程序没有相关权限控制可忽略。
这个Active属性用来表示将要使用的本地化语言:
/// <summary>
/// 获取当前报表本地化设置('CIMS本地化设置' 除外)
/// </summary>
public ReportLocalizableEnum Active
{
get
{
if (CanActive == false) return ReportLocalizableEnum.Default;
if (m_active == ReportLocalizableEnum.System) m_active = GetReportLocalizableWithSystem;
return m_active; //来自存储参数
}
}
这些属性,函数很关键,主要收集可以本地化的报表组件(未定义DataField属性的):
/// <summary>
/// 要本地化的报表控件集合,用于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类(用于收录组件的相关参数):
public class 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;
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,然后遍历集合并:
//GetLocalText函数很重要!
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 还不能本地化):
