用過 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>


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>


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.ReportLocalizable 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 ne("報表成員未指定!"); }


m_reportID = reportID;

m_activeDBForm = dbFormName;


this.OnInit();

}


protected void OnInit()


...{

if (Common.Common.get_CIMSReadOnly(false)) return;


reportLocal = new CIMS.Document.ReportLocalizable(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);

&n 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>


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



#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 != nul.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 ......
}
最終報表本地化預覽:
