解析大型.NET ERP系统 窗体、查询、报表二次开发
详细介绍Enterprise Solution 二次开发的流程步骤,主要包括数据输入窗体(Entry Form),查询(Query/Enquiry),报表(Report)三个重要的二次开发项目。
数据输入窗体开发 Entry Form
当涉及到字段的增加或增加数据库表时,开发新功能界面有以下几个主要步骤。
1 修改数据库增加表或字段,用LLBL Gen 生成新的实体映射类型定义文件。
LLBL Gen 可检测到字段的变化,增加字段或减少字段可自动更新实体映射定义文件。需要设定参数选项,如下图所示:
LLBL Gen 在生成实体的属性时,对于数据库字段可为空值的类型,会生成.NET的可空类型(nullable)类型。在我的实际项目中常常会取消这一特性,LLBL Gen会智能的知道实体哪些属性值发生变化,对于可空类型映射的属性不会生成UPDATE语句块,让它保持数据库中的空值即可(NULL)。
2 增加功能编码,增加权限,增加菜单项。
以销售合同功能(SLSOCE)为例,通过下面的脚本可增加系统功能(ADFUNC), 增加菜单项(ADMNUD)和增加权限(ADAUTD)。
- @LastLineNo INTEGER,
- @ModuleCode NVARCHAR(4),
- @FunctionCode NVARCHAR(8),
- @FunctionDesc NVARCHAR(40),
- @SeriesCode NVARCHAR(8),
- @SeriesOption NVARCHAR(1)
- SET @ModuleCode = 'SLSO'
- SET @FunctionCode = 'SLSOCE'
- SET @FunctionDesc = N'Sales Contract Entry'
- SET @SeriesCode = ''
- SET @SeriesOption = N'N'
- -- Check for Function
- WHERE [Module_Code] = @ModuleCode
- INSERT [ADFUNC]
- ([Module_Code], [Function_No], [Function_Code], [Description],
- [Suspended], [Series_Option], [Series_Code],
- [Created_Date], [Created_By], [Revised_Date], [Revised_By],
- [OWNER_BRANCH],[SOURCE_BRANCH],[Icon])
- (@ModuleCode, @LastLineNo, @FunctionCode, @FunctionDesc, N'N', @SeriesOption, @SeriesCode,
- GETDATE(), 'MIS', GETDATE(), 'MIS', N'', N'','')
- INSERT [ADAUTD]
- ([User_Group], [Module_Code], [Function_No], [Function_Code], [Description],
- [Suspended], [Allow_Read], [Allow_Create], [Allow_Update], [Allow_Delete], [Allow_Print],
- [Allow_Post], [Allow_All_Tran])
- ('SYSADM', @ModuleCode, @LastLineNo, @FunctionCode, @FunctionDesc,
- 'N' ,'Y', 'Y', 'Y', 'Y', 'Y',
- 'Y', 'Y')
- INSERT [ADMNUD]
- ([User_Group], [Process_Code], [Function_Code], [Description], [Menu_Type], [Menu_Code],
- [Response_Type], [Suspended])
- ('SYSADM', 'M3_REP', @FunctionCode, @FunctionDesc, 'DOC', '',
- 'STDFUNC', 'N')
使用SQL语句的好处是可支持自动化更新部署,当系统检查到有版本更新时可自动跑SQL语句,当客户数量比较多时这个方法可提高效率。
3 生成接口文件与接口实现类。
Code Smith模板化的代码生成极大的方便了代码生成功能的开发,参考如下的接口模板文件Interface.cst文件,源文件如下所示:
- <%@ CodeTemplate Language="C#" TargetLanguage="C#" Src="" Inherits="" Debug="True" Description="Template description here." %>
- <%@ Property Name="EntityPicker" Type="ISL.Extension.EntityPickerProperty" Optional="False" Category="Project" Description="This property uses a custom modal dialog editor." %>
- <%@ Property Name="AssemblyFile" Type="System.String" Default="" Optional="False" Category="Project" Description=""
- Editor="System.Windows.Forms.Design.FileNameEditor"%>
- <%@ Assembly Name="System.Data" %>
- <%@ Import Namespace="System.Data" %>
- <%@ Assembly Name="ISL.Empower.Extension" %>
- <%@ Import Namespace="ISL.Extension" %>
- <%@ Import Namespace="System.Collections.Generic" %>
- <%@ Assembly Name="SD.LLBLGen.Pro.ORMSupportClasses.NET20" %>
- <%@ Import Namespace="SD.LLBLGen.Pro.ORMSupportClasses" %>
- <script runat="template">
- public string EntityName
- {
- get
- {
- return EntityPicker.EntityName;
- }
- }
- public string ShortEntityName
- {
- get
- {
- return EntityName.Substring(0,EntityName.Length-6);
- }
- }
- public string FullEntityName
- {
- get
- {
- return string.Format("{0}.EntityClasses.{1}", BusinessLogicProjectName, EntityName);
- }
- }
- private string _businessLogicProjectName;
- public string BusinessLogicProjectName
- {
- get
- {
- if(string.IsNullOrWhiteSpace(_businessLogicProjectName))
- _businessLogicProjectName=EntityClassHelper.PrefixProjectName(AssemblyFile);
- return _businessLogicProjectName;
- }
- }
- public string EntityParamerList
- {
- get
- {
- IEntity2 policy = EntityClassHelper.GetEntityObject(AssemblyFile, EntityPicker.EntityName);
- string parm = string.Empty;
- List<string> parms=new List<string>();
- foreach (IEntityField2 field in policy.PrimaryKeyFields)
- {
- parm = string.Format("{0} {1}", field.DataType.Name, field.Name);
- parms.Add(parm);
- }
- return string.Join(",", parms.ToArray());
- }
- }
- public string EntityLowercaseName
- {
- get
- {
- return EntityPicker.EntityName.Substring(0, 1).ToLower() + EntityPicker.EntityName.Substring(1);
- }
- }
- </script>
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Text;
- using SD.LLBLGen.Pro.ORMSupportClasses;
- using <%=BusinessLogicProjectName%>;
- using <%=BusinessLogicProjectName%>.FactoryClasses;
- using <%=BusinessLogicProjectName%>.EntityClasses;
- using <%=BusinessLogicProjectName%>.HelperClasses;
- using <%=BusinessLogicProjectName%>.InterfaceClasses;
- using <%=BusinessLogicProjectName%>.DatabaseSpecific;
- namespace <%=BusinessLogicProjectName%>.InterfaceClasses
- {
- public interface I<%=ShortEntityName%>Manager
- {
- <%=EntityName%> Get<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>);
- <%=EntityName%> Get<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>,IPrefetchPath2 prefetchPath);
- <%=EntityName%> Get<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>,IPrefetchPath2 prefetchPath,ExcludeIncludeFieldsList fieldList);
- EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket);
- EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket,ISortExpression sortExpression);
- EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket,ISortExpression sortExpression, IPrefetchPath2 prefetchPath);
- EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
- <%=EntityName%> Save<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%>);
- <%=EntityName%> Save<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%> ,EntityCollection entitiesToDelete);
- <%=EntityName%> Save<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%>, EntityCollection entitiesToDelete, string seriesCode);
- void Delete<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%>);
- bool Is<%=ShortEntityName%>Exist(Guid sessionId,<%=EntityParamerList %>);
- bool Is<%=ShortEntityName%>Exist(Guid sessionId,IRelationPredicateBucket filterBucket);
- int Get<%=ShortEntityName%>Count(Guid sessionId,IRelationPredicateBucket filterBucket);
- <%=EntityName%> Clone<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>);
- void Post<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>);
- void Post<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%>);
- }
- }
在Code Smith代码生成器中跑Interface.cst模板,可得到如下的接口文件:
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.Text;
- using SD.LLBLGen.Pro.ORMSupportClasses;
- using ISL.BusinessLogic;
- using ISL.BusinessLogic.FactoryClasses;
- using ISL.BusinessLogic.EntityClasses;
- using ISL.BusinessLogic.HelperClasses;
- using ISL.BusinessLogic.InterfaceClasses;
- using ISL.BusinessLogic.DatabaseSpecific;
- namespace ISL.BusinessLogic.InterfaceClasses
- {
- public interface ISalesContractManager
- {
- SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo);
- SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath);
- SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
- EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket);
- EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression);
- EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath);
- EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
- SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
- SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity, EntityCollection entitiesToDelete);
- SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity, EntityCollection entitiesToDelete, string seriesCode);
- void DeleteSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
- bool IsSalesContractExist(Guid sessionId, String ContractNo);
- bool IsSalesContractExist(Guid sessionId, IRelationPredicateBucket filterBucket);
- int GetSalesContractCount(Guid sessionId, IRelationPredicateBucket filterBucket);
- SalesContractEntity CloneSalesContract(Guid sessionId, String ContractNo);
- void PostSalesContract(Guid sessionId, String ContractNo);
- void PostSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
- }
- }
接口的实现类(Manager.cst) 也只需要借助于Code Smith生成即可。生成完成之后,需要增加主从表的处理,比如主表的保存和删除方法需要做少量代码修改。
保存方法增加单据编码功能代码,如下代码片段所示:
- public SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity SalesContract, EntityCollection entitiesToDelete, string seriesCode)
- {
- string currentRefNo = string.Empty;
- using (DataAccessAdapterBase adapter = GetCompanyDataAccessAdapter(sessionId))
- {
- try
- {
- adapter.StartTransaction(IsolationLevel.ReadCommitted, "Save SalesContract");
- if (SalesContract.IsNew && seriesCode != string.Empty)
- {
- currentRefNo = SalesContract.ContractNo;
- IDocumentSerializationManager serializationManager = ClientProxyFactory.CreateProxyInstance<IDocumentSerializationManager>();
- SalesContract.ContractNo = serializationManager.GetNextSerialNo(sessionId, seriesCode, SalesContract.ContractNo, SalesContract);
- }
保存方法增加子表的保存方法
- ISalesContractDetailManager contractDetailManager = ClientProxyFactory.CreateProxyInstance<ISalesContractDetailManager>();
- if (entitiesToDelete != null)
- {
- foreach (IEntity2 entity in entitiesToDelete)
- {
- if (entity is SalesContractDetailEntity)
- contractDetailManager.DeleteSalesContractDetail(sessionId, (SalesContractDetailEntity)entity);
- }
- }
- adapter.SaveEntity(SalesContract, true, false);
- foreach (SalesContractDetailEntity salesContractDetailEntity in SalesContract.SalesContractDetail)
- {
- contractDetailManager.SaveSalesContractDetail(sessionId, salesContractDetailEntity);
- }
遵守数据库表保存的基本原则,保存时先保存主表,再保存子表,删除时是先删除子表,再删除主表。
删除方法增加删除子表的代码:
- public void DeleteSalesContract(Guid sessionId, SalesContractEntity SalesContract)
- {
- using (DataAccessAdapter adapter = GetCompanyDataAccessAdapter(sessionId))
- {
- if (!adapter.IsEntityExist<SalesContractEntity>(SalesContract))
- return;
- try
- {
- adapter.StartTransaction(IsolationLevel.ReadCommitted, "Delete SalesContract");
- ISalesContractDetailManager contractDetailManager = ClientProxyFactory.CreateProxyInstance<ISalesContractDetailManager>();
- foreach (SalesContractDetailEntity salesContractDetailEntity in SalesContract.SalesContractDetail)
- {
- contractDetailManager.DeleteSalesContractDetail(sessionId, salesContractDetailEntity);
- }
LLBL Gen没有采取级联的方式来做数据的删除,也不推荐这样处理,这样对于数据的验证相对难于处理。数据库在确认删除与应用程序的验证方法方面难于沟通,所以LLBL Gen 推荐实体相关的操作都由应用程序控制,要删除数据,要验证逻辑,都交给应用程序来实现。
4 增加界面文件,拖放控件,绑定数据源,用界面代码生成器(EntryForm.cst)生成界面实现文件。
自从用上了Visual Studio 2010, 窗体设计器的性能比之前的版本提升很大,Visual Studio 2012/2013的窗体设计器性能更加卓越,期待着即将发布的Visual Studio 2015能持续改善窗体设计器的性能。Visual Studio在全球有众多的客户,提高性能就意味着加快工作效率,也就是实现了节约能源(电力消耗),Google也有很多程序改善,提高程序性能可环保,节约电能开支。
从Visual Studio 2010迁移到Visual Studio 2012/2013的一个很主要的理由也是因为它的窗体设计器效率高。
再来看一下销售合同界面的主要代码,下面的代码是用Code Smith模板生成的,实现了界面代码自动生成:
- [FunctionCode("SLSOCE")]
- public partial class SalesContractEntry : EntryForm
- {
- private ISalesContractManager _salesContractEntityManager = null;
- private SalesContractEntity _salesContractEntity = null;
- public SalesContractEntry()
- {
- InitializeComponent();
- }
- protected override void OnLoad(EventArgs e)
- {
- if (!DesignMode)
- this._salesContractEntityManager = ClientProxyFactory.CreateProxyInstance<ISalesContractManager>();
- base.OnLoad(e);
- }
- protected override void InitNavigator(InitNavigatorArgs args)
- {
- base.InitNavigator(args);
- args.SortExpression.Add(SalesContractFields.ContractNo | SortOperator.Ascending);
- args.PredicateBucket.PredicateExpression.Add(SalesContractFields.Closed == false);
- }
- protected override EntityBase2 LoadData(Dictionary<string, string> refNo)
- {
- base.LoadData(refNo);
- string ContractNo = string.Empty;
- if (refNo.TryGetValue("ContractNo", out ContractNo))
- {
- IPrefetchPath2 prefetchPath = new PrefetchPath2((int) EntityType.SalesContractEntity);
- prefetchPath.Add(SalesContractEntity.PrefetchPathSalesContractDetail);
- _salesContractEntity = _salesContractEntityManager.GetSalesContract(Shared.CurrentUserSessionId, ContractNo, prefetchPath);
- }
- else
- {
- _salesContractEntity = new SalesContractEntity();
- }
- return _salesContractEntity;
- }
- protected override void InitializeLayout()
- {
- base.InitializeLayout();
- gridSalesOrder.OverrideReadOnlyAppearance(SalesContractDetailFields.OrderDate.Name,true);
- gridSalesOrder.OverrideReadOnlyAppearance(SalesContractDetailFields.DueDate.Name, true);
- gridSalesOrder.OverrideReadOnlyAppearance(SalesContractDetailFields.Closed.Name, true);
- gridSalesOrder.OverrideAllowEditForNewOnlyColumn(SalesContractDetailFields.EntryNo.Name, true);
- }
- protected override void BindControls(EntityBase2 entity)
- {
- base.BindControls(entity);
- this.salesContractBindingSource.DataSource = entity;
- }
- protected override EntityBase2 Add()
- {
- base.Add();
- this._salesContractEntity = new SalesContractEntity();
- return _salesContractEntity;
- }
- protected override EntityBase2 Save(EntityBase2 entityToSave, EntityCollection entitiesToDelete)
- {
- SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToSave;
- this._salesContractEntity = this._salesContractEntityManager.SaveSalesContract(Shared.CurrentUserSessionId, SalesContractEntity, entitiesToDelete, SeriesCode);
- return this._salesContractEntity;
- }
- protected override void Delete(EntityBase2 entityToDelete)
- {
- base.Delete(entityToDelete);
- SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToDelete;
- this._salesContractEntityManager.DeleteSalesContract(Shared.CurrentUserSessionId, SalesContractEntity);
- }
- protected override object Clone(Dictionary<string, string> refNo)
- {
- base.Clone(refNo);
- return null;
- }
- protected override void ReleaseResources()
- {
- base.ReleaseResources();
- try
- {
- _salesContractEntity = null;
- _salesContractEntityManager = null;
- }
- catch
- {
- }
- }
- private void btnPost_Click(object sender, EventArgs e)
- {
- this.PerformPost(true);
- }
- protected override void Post(EntityBase2 entityToPost)
- {
- base.Post(entityToPost);
- SalesContractEntity resignEntity = entityToPost as SalesContractEntity;
- _salesContractEntityManager.PostSalesContract(Shared.CurrentUserSessionId, resignEntity);
- ISL.WinUI.Shared.ShowInfo("Transaction ({0}) is posted successfully", new object[] { resignEntity.ContractNo });
- }
- }
编译运行一下程序,如下所示,一个基本的增删查改的功能就完成了,开发效率非常高,代码自动化程度也高。
5 增加类型初始化,验证,查找与钻取,自动带值,主从表事件关联
在实体类型定义文件中,增加初始化代码,比如单据默认值(单据的创建日期,单据的默认状态),系统默认值(单据的创建用户和期间):
- 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
- }
- InitEventHandlers();
- this.Validator = Singleton<SalesContractValidator>.Instance;
- }
增加从表默认值,比如主表的主键是参考编号,从表则用两个主键,前一个是参考编号,后一个主键是依次增长的自动序列号(10,20,30…..或1,2,3……):
- private void JobOrderAmendmentRemarks_EntityAdded(object sender, CollectionChangedEventArgs e)
- {
- if (e.InvolvedEntity.IsNew)
- {
- SalesContractDetailEntity remark = (SalesContractDetailEntity)e.InvolvedEntity;
- decimal max = 0;
- foreach (SalesContractDetailEntity jobOrderAmendmentRemark in this.SalesContractDetail)
- {
- if (jobOrderAmendmentRemark.EntryNo > max)
- {
- max = jobOrderAmendmentRemark.EntryNo;
- }
- }
- remark.EntryNo = max + Shared.CompanySetting.AutoIncBy;
- }
- }
如果你有Enterprise Solution 开发框架的例子代码,这些代码都是可以从原有的文件中直接拷贝过来稍做修改后即可,无任何技巧,唯手熟练。
查找与钻取
给客户编号属性值增加查找,设置Lookup属性即可。
增加查找一般都需要增加验证,增加客户编号验证。
- [Serializable]
- public partial class SalesContractValidator : ValidatorBase
- {
- // Add your own validation code between the two region markers below. You can also use a partial class and add your overrides in that partial class.
- // __LLBLGENPRO_USER_CODE_REGION_START ValidationCode
- public override bool ValidateFieldValue(IEntityCore involvedEntity, int fieldIndex, object value)
- {
- bool result = base.ValidateFieldValue(involvedEntity, fieldIndex, value);
- if (!result) return false;
- switch ((SalesContractFieldIndex) fieldIndex)
- {
- case SalesContractFieldIndex.CustomerNo:
- return this.ValidateCustomerNo((string) value);
- }
- return true;
- }
- private bool ValidateCustomerNo(string value)
- {
- if (!string.IsNullOrEmpty(value))
- {
- ICustomerManager customerManager = ClientProxyFactory.CreateProxyInstance<ICustomerManager>();
- customerManager.ValidateCustomerNo(Shared.CurrentUserSessionId, value);
- }
- return true;
- }
增加客户编号钻取,在界面中双击此控件可直接跳转到相应的主档功能。
这样就完成了新功能的开发,想要做到基于EntryForm应用程序的高效率开发,必须通晓框架的功能,知道在哪里插入什么样的代码,这在Enterprise Solution技术培训中会详细讲解。
查询窗体 Query/Enquiry
有两种类型的查询功能,一个是数据输入窗体查询,这种查询需要继承原有的输入窗体(EntryForm),修改属性即可实现,参考下面的销售合同查询功能的实现。
销售合同查询功能全部的代码如下,只需要重写几个方法即可:
- [FunctionCode("SLSOCQ")]
- public partial class SalesContractEnquiry : SalesContractEntry
- {
- public SalesContractEnquiry()
- {
- InitializeComponent();
- this.SupportAdd = false;
- this.SupportEdit = false;
- this.SupportDelete = false;
- }
- protected override void InitNavigator(FunctionFormBase.InitNavigatorArgs args)
- {
- args.PredicateBucket.PredicateExpression.Add(SalesContractFields.Closed == true);
- if (!AllowViewAllTransaction)
- args.PredicateBucket.PredicateExpression.Add(SalesContractFields.CreatedBy ==ISL.BusinessLogic.Shared.CurrentUserSession.UserId);
- args.SortExpression.Add(SalesContractFields.ContractNo | SortOperator.Ascending);
- }
- protected override void InitializeLayout()
- {
- base.InitializeLayout();
- this.btnClose.Visible = false;
- this.txtContractNo.Lookup.FilterName = "Posted";
- }
- }
第二种查询是自定义查询,可查询任意条件过滤的多个表的数据。
EntryForm没有使用LLBL Gen的Typeed Lists或Typed Views,而是通过自定义的查询(Custom Query)来实现数据查询。
来看一下送货日记的查询界面,这个界面的主要布局是上面是过滤条件,下面是要根据过滤条件查询到的结果数据。
送货日记的全部源代码如下所示,
- [FunctionCode("SQMEGS")]
- public sealed partial class ShipmentJournalEnquiry : EnquiryForm
- {
- private IUserDefinedQueryManager _udqManager;
- private bool _show;
- public ShipmentJournalEnquiry()
- {
- InitializeComponent();
- }
- protected override void InitializeLayout()
- {
- base.InitializeLayout();
- this.grdShipment.DisplayLayout.Bands[0].Columns["Year"].Format = "####";
- this.grdShipment.DisplayLayout.Bands[0].Columns["Month"].Format = "##";
- }
- protected override void OnLoad(EventArgs e)
- {
- base.OnLoad(e);
- if (!DesignMode)
- {
- _udqManager = ClientProxyFactory.CreateProxyInstance<IUserDefinedQueryManager>();
- _show = true;
- }
- }
- protected override void InitNavigator(ISL.WinUI.Forms.FunctionFormBase.InitNavigatorArgs args)
- {
- base.InitNavigator(args);
- if (_show)
- this.FetchShipment();
- }
- private void FetchShipment()
- {
- IPredicateExpression predicateExpression = null;
- IRelationPredicateBucket filterBucket = new RelationPredicateBucket();
- if (Shared.CurrentUserSession.AllowAccessAllCustomers)
- filterBucket.PredicateExpression.Add(Shared.GetAllowedCustomerNoPredicateExpression(ShipmentFields.CustomerNo));
- if (!AllowViewAllTransaction)
- filterBucket.PredicateExpression.Add(ShipmentFields.CreatedBy == Shared.CurrentUser.UserId);
- predicateExpression = this.efcCustomerNo.GetPredicateExpression();
- if (predicateExpression != null)
- filterBucket.PredicateExpression.Add(predicateExpression);
- predicateExpression = this.efcShipFrom.GetPredicateExpression();
- if (predicateExpression != null)
- filterBucket.PredicateExpression.Add(predicateExpression);
- predicateExpression = this.efcPostedFilter.GetPredicateExpression();
- if (predicateExpression != null)
- filterBucket.PredicateExpression.Add(predicateExpression);
- predicateExpression = this.efcReturnedFilter.GetPredicateExpression();
- if (predicateExpression != null)
- filterBucket.PredicateExpression.Add(predicateExpression);
- ResultsetFields fields = new ResultsetFields(16);
- fields.DefineField(ShipmentFields.RefNo, 0);
- fields.DefineField(ShipmentFields.ShipmentDate, 1);
- DbFunctionCall dbYear = new DbFunctionCall("Year", new object[] { ShipmentFields.ShipmentDate });
- EntityField2 Year = new EntityField2("Year", dbYear);
- fields.DefineField(Year, 2);
- DbFunctionCall dbMonth = new DbFunctionCall("Month", new object[] { ShipmentFields.ShipmentDate });
- EntityField2 Month = new EntityField2("Month", dbMonth);
- fields.DefineField(Month, 3);
- fields.DefineField(ShipmentFields.Posted, 4);
- fields.DefineField(ShipmentFields.Returned, 5);
- fields.DefineField(ShipmentFields.CustomerNo, 6);
- fields.DefineField(ShipmentFields.CustomerName, 7);
- fields.DefineField(ShipmentFields.Ccy, 8);
- fields.DefineField(ShipmentFields.TotItemAmt, 9);
- fields.DefineField(ShipmentFields.Etd, 10);
- fields.DefineField(ShipmentFields.ShipFrom, 11);
- fields.DefineField(ShipmentFields.PostedBy, 12);
- fields.DefineField(ShipmentFields.PostedDate, 13);
- fields.DefineField(ShipmentFields.CreatedBy, 14);
- fields.DefineField(ShipmentFields.CreatedDate, 15);
- ISortExpression sortExpression = new SortExpression(ShipmentFields.RefNo | SortOperator.Ascending);
- DataTable table = QueryHelper.GetQueryResult(Shared.CurrentUserSession.CompanyCode, fields, filterBucket, sortExpression, null, true);
- this.shipmentBindingSource.DataSource = table;
- this.grdShipment.SetDataBinding(this.shipmentBindingSource, null, true, true);
- }
- private void btnRefresh_Click(object sender, EventArgs e)
- {
- this.PerformRefresh();
- //clear filter
- this.efcShipFrom.Clear();
- this.efcCustomerNo.Clear();
- }
- private void grdShipment_BeforeDrillDown(object sender, DrillDownEventArgs e)
- {
- if (Shared.StringCompare(this.grdShipment.ActiveCell.Column.Key, ShipmentFields.RefNo.Name) == 0)
- {
- DataRowView row = this.shipmentBindingSource.Current as DataRowView;
- if (row != null)
- {
- bool isPosted = Convert.ToBoolean(row[ShipmentFields.Posted.Name]);
- e.DrillDownValue.FunctionCode = (isPosted ? "SQMETS" : "SLSHSE");
- }
- }
- }
- protected override void OnBeforeGridExport(CancelableGridDataExportEventArgs args)
- {
- base.OnBeforeGridExport(args);
- args.Grid = this.grdShipment;
- }
- protected override void OnBeforeGridPrint(CancelableGridDataExportEventArgs args)
- {
- base.OnBeforeGridPrint(args);
- args.Grid = this.grdShipment;
- }
- protected override void ReleaseResources()
- {
- try
- {
- this._udqManager = null;
- this.btnRefresh.Click -= new EventHandler(btnRefresh_Click);
- this.grdShipment.BeforeDrillDown -= new ISL.WinUI.DrillDownEventHandler(this.grdShipment_BeforeDrillDown);
- }
- catch
- {
- }
- base.ReleaseResources();
- }
这个功能的数据读取代码没有封装到BackgroundWorker后台线程组件中,当数据量多时会发生界面死锁,用户体验性不好。
报表 Report
水晶报表已经是报表行业的工业标准,完善的功能与强大的客户化开发功能,水晶报表的市场占有率一直很高。微软的后起之秀Reporting Services也相当优秀,Enterprise Solution对这两种类型的报表都有完备的支持。
Enterprise Solution解决了报表开发中令开发人员头疼的界面传参问题,它可以依据一个设定自动生成报表参数界面,通过ReportViewer自动传递到报表文件中。
如下图所示,当开发完成水晶报表之后,需要在报表对话框中增加一个参数设定,用于生成报表的参数:
依据上面的三个参数,报表执行引擎产生一个参数输入界面,如下图所示
用户在界面中输入值,点击查看按钮,报表引擎将用户输入的值传递到报表中,这个过程不需要开发人员的编程或设定。
此外,报表引擎还处理了水晶报表运行库的问题,报表中的标签也会自动翻译。
另外,工作流与计划任务的二次开发也相当常见,因相对复杂需要单独成篇,暂不介绍。
工作流(Workflow)
计划任务(Scheduling)
解析大型.NET ERP系统 窗体、查询、报表二次开发的更多相关文章
- 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器
企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...
- 解析大型.NET ERP系统 十三种界面设计模式
成熟的ERP系统的界面应该都是从模板中拷贝出来的,各类功能的界面有规律可遵循.软件界面设计模式化或是艺术性的创作,我认可前者,模式化的界面客户容易举一反三,降低学习门槛.除了一些小部分的功能界面设计特 ...
- 解析大型.NET ERP系统架构设计 Framework+ Application 设计模式
我对大型系统的理解,从数量上面来讲,源代码超过百万行以上,系统有超过300个以上的功能,从质量上来讲系统应该具备良好的可扩展性和可维护性,系统中的功能紧密关联.除去业务上的复杂性,如何设计这样的一个协 ...
- 解析大型.NET ERP系统 设计异常处理模块
异常处理模块是大型系统必备的一个组件,精心设计的异常处理模块可提高系统的健壮性.下面从我理解的角度,谈谈异常处理的方方面面.我的设计仅仅限定于Windows Forms,供参考. 1 定义异常类型 . ...
- 解析大型.NET ERP系统 权限模块设计与实现
权限模块是ERP系统的核心模块之一,完善的权限控制机制给系统增色不少.总结我接触过的权限模块,以享读者. 1 权限的简明定义 ERP权限管理用一句简单的话来说就是:谁 能否 做 那些 事. 文句 含义 ...
- 解析大型.NET ERP系统 灵活复杂的界面控件Infragistics WinForms
Infragistics 是.NET平台优秀的控件供应商,囊括了WinForms,ASP.NET,Silverlight,WPF,Windows Phone等所有关于微软.NET技术的界面控件.借助于 ...
- 解析大型.NET ERP系统 查找与钻取
查找 Lookup 窗体是一个容器,也可以把TextBox,Button也看成是一个容器,可以往容器里面添加按钮. 参考下面的实现代码,给TextBox增加查找按钮. var btn = new Bu ...
- 解析大型.NET ERP系统 20条数据库设计规范
数据库设计规范是个技术含量相对低的话题,只需要对标准和规范的坚持即可做到.当系统越来越庞大,严格控制数据库的设计人员,并且有一份规范书供执行参考.在程序框架中,也有一份强制性的约定,当不遵守规范时报错 ...
- 解析大型.NET ERP系统 单据标准(新增,修改,删除,复制,打印)功能程序设计
ERP系统的单据具备标准的功能,这里的单据可翻译为Bill,Document,Entry,具备相似的工具条操作界面.通过设计可复用的基类,子类只需要继承基类窗体即可完成单据功能的程序设计.先看标准的销 ...
随机推荐
- json排序 摘自百度
var sortBy = function (filed, rev, primer) { rev = (rev) ? -1 : 1; return function (a, b) { ...
- android camera 自定义开发
1.检测是否有摄像头 /** Check if this device has a camera */ private boolean checkCameraHardware(Context cont ...
- android开发学习之Layer List
Android中drawable分为Bitmap File.Nine-Patch File.Layer List.State List.Level List.Transition Drawable.I ...
- ASP.Net MVC跳转,分为form的submit提交跳转和ajax跳转
1,用jquery ajax跳转的话,需要在前台用window.location("跳转网址")来跳转,在success后使用 2,用原声的form的submit来跳转,如下图 3 ...
- 道路翻新 (Revamping Trails, USACO 2009 Feb)
题意:给定m<=50000的1-n有联通的图,求最多可以使K<=20条边变为0的情况下的最短路是多少.. 思路:简单的分层图最短路,对于每个点拆成K个点.. 然后求一边最短路.. code ...
- unity 状态机 + svn + 码云 上篇
最近刚找到在实习,忙于公司一个c++ 项目 ,一直想写博客来着,没时间写今天熬夜打算先献上自己前几天自己封装的一个fsm状态机 话不多说,直接上正题,这篇博客主要是在学校的时候状态机一直使用的是pla ...
- 解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译)
解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译) http://improve.dk/orcamdf-now-supports-databases-with-mult ...
- 一个事务复制的bug--更新丢失
有两种情况会造成更新丢失,第一种是不正确的设置,例如外键或触发器的“Not For Replication” (NFR)属性没有开启.详情请参考http://blogs.msdn.com/b/apgc ...
- .NET Core下使用gRpc公开服务(SSL/TLS)
一.前言 前一阵子关于.NET的各大公众号都发表了关于gRpc的消息,而随之而来的就是一波关于.NET Core下如何使用的教程,但是在这众多的教程中基本都是泛泛而谈,难以实际在实际环境中使用,而该篇 ...
- .Net Core下如何管理配置文件
一.前言 根据该issues来看,System.Configuration在.net core中已经不存在了,那么取而代之的是由Microsoft.Extensions.Cnfiguration.XX ...