ERP系統的單據具備標准的功能,這里的單據可翻譯為Bill,Document,Entry,具備相似的工具條操作界面。通過設計可復用的基類,子類只需要繼承基類窗體即可完成單據功能的程序設計。先看標准的銷售合同單據界面:
本篇通過銷售合同單據功能,依次講解編程要點,供參考。
1 新增 Insert
窗體有二種狀態,一種是編輯狀態,別一種是數據瀏覽狀態,區別在於編輯狀態的窗體數據被修改(dirty),在窗體關閉時需要保存數據。點擊工具條的新增(Insert)按鈕,窗體進入編輯狀態。新增狀態需要對窗體所編輯的單據設置默認值。一般我們在實體映射文件中設置默認值,參考下面的例子代碼:
public partial class SalesContractEntity { protected override void OnInitialized() { base.OnInitialized(); // Assign default value for new entity if (Fields.State == EntityState.New) { #region DefaultValue // __LLBLGENPRO_USER_CODE_REGION_START DefaultValue this.Fields[(int) SalesContractFieldIndex.Closed].CurrentValue = false; // __LLBLGENPRO_USER_CODE_REGION_END #endregion } } }
也可以考慮在窗體中做默認值設定。當遇到這樣一種場景,兩個功能對應同一個實體類型,則需要在界面中根據需要初始化值,參考下面的程序片段。
protected override EntityBase2 Add() { base.Add(); this._inventoryMovement = new InventoryMovementEntity(); this._inventoryMovement.TranType = GetStringValue(InventoryTransactionType.Movement); return this._inventoryMovement; }
2 保存 Save
窗體基類檢測到界面中的控件值被修改過,窗體狀態變為編輯狀態,點擊保存按鈕執行保存方法。保存方法的主要內容是將數據源控件(BindingSource)所綁定的控件值更新到它映射的實體中,再調用窗體保存方法保存實體。
protected override EntityBase2 Save(EntityBase2 entityToSave, EntityCollection entitiesToDelete) { SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToSave; this._salesContractEntity = this._salesContractEntityManager.SaveSalesContract( SalesContractEntity, entitiesToDelete, SeriesCode); return this._salesContractEntity; }
entityToSave是數據源控件綁定的實體類型,在保存完成后,這個值再次綁定到數據源控件中。
3 刪除 Delete
窗體只有在瀏覽狀態時,才可以點擊刪除按鈕,刪除按鈕的內容是獲取窗體數據源控件綁定的實體,調用窗體刪除方法。
protected override void Delete(EntityBase2 entityToDelete) { base.Delete(entityToDelete); SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToDelete; this._salesContractEntityManager.DeleteSalesContract(SalesContractEntity); }
對實體的任何操作,都會跑實體驗證類型,比如在保存時,需要驗證主鍵值是否已經保存過,參考下面的代碼。
public override void ValidateEntityBeforeSave(IEntityCore involvedEntity) { base.ValidateEntityBeforeSave(involvedEntity); SalesContractEntity salesContract = (SalesContractEntity)involvedEntity;
if (string.IsNullOrEmpty(salesContract.ContractNo)) throw new EntityValidationException("Contract No. is required"); if (string.IsNullOrEmpty(salesContract.CustomerNo)) throw new EntityValidationException("Customer No. is required"); if (salesContract.IsNew) { ISalesContractManager salesContractManager = CreateProxyInstance<ISalesContractManager>(); if (salesContractManager.IsSalesContractExist(salesContract.ContractNo)) throw new RecordDuplicatedException(salesContract.ContractNo, "Cotract No. is already used"); } }
4 復制 Clone
窗體支持兩種復制方法,復制當前加載的值,復制其它對象的值。對象值復制完成后,需用重置新的對象的主鍵值,讓它為空或是為默認值,供用戶修改。復制其它對象的值需要彈出對象選擇窗體。這兩種復制都需要注意復制完后,重置對象初始化默認值。因為復制時采用的是深拷貝,沒有跑對象初始化值。
protected override object Clone(Dictionary<string, string> refNo) { base.Clone(refNo); string receiptRefNo; refNo.TryGetValue("ContractNo", out receiptRefNo); if (string.IsNullOrEmpty(receiptRefNo)) { using (ILookupForm lookup = GetLookupForm("SalesContractLookup")) { if (!AllowViewAllTransaction) lookup.PredicateBucket = new RelationPredicateBucket(SalesContractFields.CreatedBy == Shared.CurrentUser.UserId); lookup.SetCurrentValue(CurrentRefNo); if (lookup.ShowDialog() != DialogResult.OK)
return null; receiptRefNo = lookup.GetFirstSelectionValue(); } } if (!string.IsNullOrEmpty(receiptRefNo)) { this._salesContractEntity = this._salesContractEntityManager.CloneSalesContract(receiptRefNo); return this._salesContractEntity; } return null;
}
注意到這些方法全部是以override重寫的方式出現,被基類調用。每個方法都運行在后台線程控件BackgroundWorker線程中,所以都不能操作界面控件。
5 記錄瀏覽 Record Navigator
主要用於工具條的前四個按鈕,分別對應第一筆數據,前一筆數據,下一筆數據,最后一筆數據。工具條瀏覽需要設定窗體的NavigateBindingSource屬性,傳入一個空白的BindingSource控件或是一個裝載頁面所有數據的BindingSource控件,工具條瀏覽方法重寫參考下面的程序片段。
protected override void InitNavigator(InitNavigatorArgs args) { base.InitNavigator(args); args.SortExpression.Add(SalesContractFields.ContractNo | SortOperator.Ascending); args.PredicateBucket.PredicateExpression.Add(SalesContractFields.Closed == false); }
6 記錄過帳 Record Post
主要用於業務單據過帳邏輯,基類的方法為空方法,不同的業務單據有不同的邏輯定義,沒有可復用的代碼。
protected override void Post(EntityBase2 entityToPost) { base.Post(entityToPost); SalesContractEntity resignEntity = entityToPost as SalesContractEntity; _salesContractEntityManager.PostSalesContract(resignEntity); }
這里的過帳可以理解為確認,批核,不可更改的意思。在有些系統中叫送審,審核。
7 打印 Print
一般在設計視圖綁定當前窗體對應的水晶報表文件以及要傳入的參數。也可以通過重寫打印方法傳入傳數值。
protected override void Print(ref Dictionary<string, SelectionFormula> selectionFormulas, ref List<FormulaField> formulaFields, ref List<ParameterField> parameterFields) { base.Print(ref selectionFormulas, ref formulaFields, ref parameterFields); }
重寫方法常用於動態指定報表文件,或是動態的參數值。不推薦在代碼中這樣寫,這樣做導致每次都需要重新編譯和分發程序,推薦在水晶報表中做公式,在分發時只需要拷貝文件即可。

