用過 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 ......
}
最終報表本地化預覽: