从零开始

概述

      我承认标题有点哗众取宠,实在想不出好名字,随便起的。前面讲了很多理论性的东西,大家要把他们用起来估计还有一定的难度。这里我把刚做完的一张界面作为范例,详细解说下怎么进行CSMS2的开发。

 

真的从零开始了

首先我们需要创建一个窗体,一般情况下都插入DevExpress Form v10.1,真的碰到很多人还给我插入WinForm或v6的窗体或控件。所以这里还是需要强调一下。如果有低版本的请卸载掉,免得搞错了。其次是窗体的起名,这里我们不考大家的英文,都是前两个全拼,后面首字母缩写。再加上前缀Frm。窗体都建立在某个目录下。如果不确定的,向我或自己的项目经理确认。这里我们将在BiaoWuGL下建立一个FrmFuHeDJ的窗体。

其次,给窗体写注释

拿到要做的窗体后,第一件事情就是给在头部加入如下代码:

//********************************************************
//创建日期:<创建日期,2012-05-24>
//创建作者:<张易,zhangyi@shanghai3h.com>
//功能说明: 表务工单复核登记界面
//********************************************************

 

 不多解释,这里要注意的是,谁做的界面把名字都写上。以前做原型的人的名字可以忽略。对于大部分重构的界面,把名字也替换掉。如果是小修小改的,直接在代码上加注释说明了。还有多一点事界面的功能描述加上,有时光看个拼音还不知道是什么界面。

然后我们将界面分成多个region

第一个是变量region,把界面上用到的属性、域成员变量都放在这这个region下。如果是属性记得首字母大写。如果可能的话直接写{get;set}。域成员变量用"_"开头。

属性:

        /// <summary>
        
/// 账号
        
/// </summary>
        public string S_CID { getset; }

域成员变量:

        private KeHuGXService.KeHuGXClient _Proxy = new KeHuGXService.KeHuGXClient();

第二个区域是事件,所有事件都放在这里,纠结点的就是构造函数了。看放在事件里的人多,那我们也放在事件里。这里很多人刚开始整理的蛮好的,到后面就乱来了。还有就是经常将一些事件的说明漏了。比如一个界面加载,如果没什么好说的,你就写个界面加载,如果有些特别要注意的,可以在上面写的在详细点。例如:

        /// <summary>
        
/// 站点改变,触发册本改变
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void ComTreeZhanDian_SelectValueChange(object sender, EventArgs e)
        {
            _Proxy.GetCeBenXXBySTAsync(ComTreeZhanDian.SelectValue);
        }

这里要说的也就是一个持之以恒了。

第三个区域是方法。如果在引擎里,大家还可以分成公共方法和私有方法等区域。在窗体里一般情况下都是私有方法。就直接放一个方法region里了。这里要注意程序为什么有了方法。没方法全事件理论上也能写窗体。不就是为了代码重用和看起来简洁吗!大家要多注意归纳总结。将公共的东西提炼出来。放到方法里。将更加通用的方法提炼出来放到基础架构中。我们的目的是在写一篇给自己和别人能看懂的“小说”。

接下来,就要真正开始开发了

 首先我们需要打开需求文档设计界面。有部分界面和以前的项目差不多,可以直接复制界面过来。做界面时注意控件的命名。首字母需要大写,其次是缩写的控件名,在后面是名称。例如TxtKeHuBH、BtnChaXun。画界面的时候注意Dock和Anchor的使用。经常看到有些界面放到宽屏或非宽屏,显示效果就不一样了。这里可能就是某个按钮做了Anchor定位,某个没做。

其次我们需要设计对应的应用层方法,用复核登记界面举例,它里面有两个按钮两个按钮一个“查询”、一个“登记”是需要和后台数据库做交互的。这时我们就需要在BIAOWUGL.svc中添加两个方法FuHeDJ_ChaXun、FuHeDJ_DengJi。命名规则是界面名称+下划线+功能名称。

        /// <summary>
        
/// 复核登记查询
        
/// </summary>
        
/// <param name="dto"></param>
        
/// <returns></returns>
        public DataTable FuHeDJ_ChaXun(FuHeDJSearchDTO dto)
        {
            return DynamicSQL.GetTable(dto);
        }

        /// <summary>
        
/// 复核登记
        
/// </summary>
        
/// <param name="message">错误消息</param>
        
/// <param name="dto">dto</param>
        
/// <param name="Entity.BW_YUYUEXX">预约信息</param>        
        
/// <param name="login">登陆信息</param>
        
/// <returns>true or false</returns>
        public bool FuHeDJ_DengJi(out string message, FuHeDJDTO dto,Entity.BW_YUYUEXX yuyuexx, Entity.LoginInfo login) 
        {
            return BiaoWuGLEngine.DengJi(out message, dto, yuyuexx, login);
        }

然后我们需要考虑应该需要哪些参数,对于查询来说。以前已经介绍过了,只需要传入对应的查询dto即可。对于复核登记。我们需要传入登记信息、预约信息,一般情况下我们也会传入登录信息,让业务逻辑层将对应的信息更新到操作人字段等。如果出现异常,通过message接受错误信息。这里要注意如下情况:

  • WCF中一定要实现对应的接口,接口中需要加入OperationContract的特性
    public class BiaoWuGL : IBiaoWuGL

 

    [ServiceContract]
    public interface IBiaoWuGL
    {
        [OperationContract]
        DataTable FuHeDJ_ChaXun(FuHeDJSearchDTO dto);

        [OperationContract]
        bool FuHeDJ_DengJi(out string message, FuHeDJDTO dto, Entity.BW_YUYUEXX yuyuexx, Entity.LoginInfo login);
    }
  • DTO的设计尽量精简

 经常看到一些庞大的DTO,美其名曰通用性强。其实这种设计完全是败笔,设想一下这里如果暴露一个BW_GONGDAN的DTO,确实也能操作。但这时候你让界面开发人员到底传多少参数给你那,哪些属性需要赋值界面开发人员根本不知道。所以dto需要尽量的精简,不要然我们的界面层充斥太多的赋值操作。另外这对开发模式其实也是一种变革。以前我们一个人从头做到尾,我想在哪里实现业务逻辑还不都是我一个人的工作。所以这种变革也涉及到开发模式,尽量安排2个人进行一张界面的开发。一个做表现层、应用层。还有一个做业务逻辑等。要不然确实有点精神分裂的感觉。

 

应用层设计好后,由于对应的业务逻辑还没有实现,我们直接在方法里return null,reutrn ture就可以了,等待编写业务逻辑的人员将代码补充进来。其实应用层到底应该放什么方法,传哪些参数,应该需要项目经理设计一下,但有的项目经理比较忙,如果这部分设计上有什么问题,而各组的项目经理又不在,可以向我咨询。

表现层开发

 首先我们要引用WCF,一般情况下引用已经有了。则我们只要对前面添加的应用层方法做更新。然后我们需要思考初识化时需要绑定的控件。代码如下:

 

        /// <summary>
        
/// 构造函数
        
/// </summary>
        public FrmFuHeDJ()
        {
            InitializeComponent();
            InitControl();
        }

 

        /// <summary>
        
/// 初始化控件
        
/// </summary>
        public void InitControl()
        {
            BindDataHelper.BindDropDownList(ClientCacheHelper.GetYHWordsByID(YHWordsType.外复原因), LueWaiFuYY,true);
            BindDataHelper.BindDropDownList(ClientCacheHelper.GetWordsByID(WordsType.外复类型), LueWaiFuLX, true);
            ComTreeZhanDian.BindS_ST();
            _Proxy.GetCeBenXXBySTCompleted += new EventHandler<KeHuGXService.GetCeBenXXBySTCompletedEventArgs>(_Proxy_GetCeBenXXBySTCompleted);
            InitGridControl();
        }

初识化控件,主要是做一些绑定,和赋默认值。对于绑定类似dropDownList的控件使用BindDataHelper类,对于站点,使用站点控件。对于绑定DropDownList就不做过多介绍了,大家看对应绑定数据的介绍。这里用到个小技巧处理站点和册本的联动。用了异步调用的方式,具体代码如下:

        /// <summary>
        
/// 站点改变,触发册本改变
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void ComTreeZhanDian_SelectValueChange(object sender, EventArgs e)
        {
            _Proxy.GetCeBenXXBySTAsync(ComTreeZhanDian.SelectValue);
        }

        /// <summary>
        
/// 异步调用册本绑定
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void _Proxy_GetCeBenXXBySTCompleted(object sender, KeHuGXService.GetCeBenXXBySTCompletedEventArgs e)
        {
            DataTable dt = e.Result;
            BindDataHelper.BindDropDownList(dt, LueCH, true);
        }

接下来我们对Grid进行默认赋值,Grid控件,请大家都拖拽MyGridControl控件,这里对以前的GridControl做了一些扩展。

 

        /// <summary>
        
/// 初始化GridControl
        
/// </summary>
        private void InitGridControl() 
        {
            List<UserControl.MyGridControl.ColumnInitInfo> lst = new List<UserControl.MyGridControl.ColumnInitInfo>();
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "客户编号"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "账号"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "册本号"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "户名"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "地址"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "口径"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "表分类"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "表类型"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "表型码"});
            lst.Add(new UserControl.MyGridControl.ColumnInitInfo { Caption = "表号"});
            GCDaiDengJWF.ColumnInitInfoCollection = lst;
            GCDaiDengJWF.HasCheckColumn = true;
            GCDaiDengJWF.IsRadio = true;
        }

由于我想不要再对GridControl的每个列做添加,所以增加了一个ColumnInitInfoCollection,默认情况下赋一下Caption就可以了,如果需要改变列宽,里面也有对应的设置。HasCheckColumn是表示是否添加Check列,IsRadio是表示是否只能单选。以后如果用到Image图标和右键功能等,我在增加一下了,总之大家用这个控件就对了。

 

做完初始化操作后我们需要对一些事件进行操作了。

查看界面我们发现有3个按钮、分别是查询、添加预约信息、登记。下面我们分别对3个按钮进行事件添加。

先来看查询,首先我们需要对查询DTO赋值,然后调用数据校验方法进行数据验证。然后将查询的结果返回,如果没有查到数据则给出提示。为了简化掉一些验证和提示的操作,这里用了一个委托方法的小技巧,简化部分代码。

            FuHeDJSearchDTO searchDTO = new FuHeDJSearchDTO();
            searchDTO = GetFuHeDJSearchDTO();
            Func<DataTable> func = () =>
            {
                BiaoWuGLService.BiaoWuGLClient client = new BiaoWuGLService.BiaoWuGLClient();
                DataTable dt = client.FuHeDJ_ChaXun(searchDTO);
                client.Close();
                return dt;
            };
            OperateHelper.Search(searchDTO, GCDaiDengJWF, func);

大家可以看到除了查询和赋值,其他的都隐藏起来了。我们在看下OperateHelper.Search做了点什么。

        /// <summary>
        
/// 简化查询操作
        
/// </summary>
        
/// <typeparam name="DTO"></typeparam>
        
/// <param name="successMessage"></param>
        
/// <param name="dto"></param>
        
/// <param name="func"></param>
        
/// <returns></returns>
        public static void Search<DTO>(DTO dto,dynamic gc, Func<DataTable> func)
        {
            string message;
            if (!DataAnnotationsEntityValidator.IsValid(dto, out message))
            {
                AlterHelper.MessageBoxShowInfo(message);
                return;
            }

            DataTable dt = func();
            if (null != dt && dt.Rows.Count > 0)
            {
                gc.DataSource = dt;
            }
            else
            {
                AlterHelper.MessageBoxShowInfo("COMMON021");
            }
        }

看代码就知道做了校验,绑定,和一些提示信息。把公共的抽取出来了。尽管我挺讨厌委托方法的语法的,但是等省略点代码我还是加上了。这里注意对WCF方法的关闭。

 

然后我们看登记事件。

要做的事情和查询差不多,都是对实体赋值,检验、调业务逻辑、最后返回结果,如果登记成功则,结果集刷新,清空登记记录。和前面一样这里也用了个委托方法。

        /// <summary>
        
/// 登记
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void BtnDengJi_Click(object sender, EventArgs e)
        {
            FuHeDJDTO dto = GetFuHeDJDTO();
            Func<string> func = () =>
            {
                string message = string.Empty;
                BiaoWuGLService.BiaoWuGLClient client = new BiaoWuGLService.BiaoWuGLClient(); 
                client.FuHeDJ_DengJi(out message, dto,(Entity.BW_YUYUEXX)ClientCacheHelper.GetCache("BW_YUYUEXX"), Entity.LoginInfo.CurrentUser);
                client.Close();
                return message;
            };
            bool result = OperateHelper.Save("BIAOWUGL001", dto, func);
            if (result)
                GVDaiDengJWF.ReDataBind();
        }

这样我们就把重复劳动分出去了,对于OperateHelper.Save操作代码如下:

        /// <summary>
        
/// 简化保存操作
        
/// </summary>
        
/// <typeparam name="DTO"></typeparam>
        
/// <param name="successMessage"></param>
        
/// <param name="dto"></param>
        
/// <param name="func"></param>
        
/// <returns></returns>
        public static bool Save<DTO>(string successMessage,DTO dto, Func<string> func)
        {
            string message = string.Empty;
            if (!DataAnnotationsEntityValidator.IsValid(dto, out message))
            {
                AlterHelper.MessageBoxShowInfo(message);
                return false;
            }

            message = func();
            if (!string.IsNullOrEmpty(message))
            {
                AlterHelper.MessageBoxShowError(message);
                return false;
            }
            else
            {
                AlterHelper.MessageBoxShowInfo(successMessage);
            }
            return true;
        }

 

 添加预约信息,其实就是show一个窗体,代码如下

        /// <summary>
        
/// 添加预约信息
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void BtnTianJiaYYXX_Click(object sender, EventArgs e)
        {
            string s = GVDaiDengJWF.GetKey("S_CID");
            if (!string.IsNullOrEmpty(s))
            {
                FrmYuYueXX frm = new FrmYuYueXX(GVDaiDengJWF.GetKey("S_CID"));
                frm.ShowDialog();
            }
            else 
            {
                AlterHelper.MessageBoxShowInfo("BIAOWUGL003");
            }
        }

这里需要说明的是一些缓存的应用,比如预约信息。用户需要保存在内存中,这个我们可以使用ClientCacheHelper将其先保存在内存里,然后界面直接用ClientCacheHelper.GetCache获取内存数据。还有就是Grid提供了一些GetKey、GetCheckInfo的公共方法用于简化获取。如果还有什么不清楚的直接来问我吧,我写文章还是不够精细的。

 

关于DOT的一些验证,可以参考数据验证章节,这样我们除了业务逻辑,界面就都完成了。个人觉得对dto的赋值还是比较麻烦,最好能调个方法自己循环去。

posted on 2012-06-07 23:02  zyi  阅读(1134)  评论(1编辑  收藏  举报

导航