好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

总结的一些经验

总结的一些经验

从开发的软件《备件仓库管理系统》

本人做Winform开发多年,孜孜不倦,略有小成,其中收集或者自己开发一些常用的东西,基本上在各个项目都能用到的一些开发经验及知识积累,现逐步介绍一些,以飨读者,共同进步。

 1、窗口【×】关闭按钮变为最小化,并在托盘提示信息

一般有些管理系统,为了防止客户随意关闭程序或者基于其他原因,一般会把 窗口【×】关闭按钮变为最小化,如大家熟悉的飞信、MSN等等,但是有些不是很熟悉的客户,最小化到托盘的时候,却不知道程序到了那里去了,因此,最小化的时候,伴随一个气泡提示信息,显得有一定的必要,如下截图所示。

 

首先在主窗体的设计界面中添加一个NotifyIcon控件,然后实现相关的代码即可。 

下面列出一些关键的代码出来,大家看了应该就知道如何实现了

           private   void  notifyMenu_Show_Click( object  sender, EventArgs e)
        {
             if  ( this .WindowState  ==  FormWindowState.Minimized)
            {
                 this .WindowState  =  FormWindowState.Maximized;
                 this .Show();
                 this .BringToFront();
                 this .Activate();
                 this .Focus();
            }
             else
            {
                 this .WindowState  =  FormWindowState.Minimized;
                 this .Hide();
            }
        }

        private   void  notifyMenu_Exit_Click( object  sender, EventArgs e)
        {
             try
            {
                 this .ShowInTaskbar  =   false ;
                Portal.gc.Quit();
            }
             catch
            {
                 //  Nothing to do.
            }
        }

         private   void  notifyIcon1_MouseDoubleClick( object  sender, MouseEventArgs e)
        {
            notifyMenu_Show_Click(sender, e);
        }

         private   void  MainForm_MaximizedBoundsChanged( object  sender, EventArgs e)
        {
             this .Hide();
        }

         ///   <summary>
         ///  缩小到托盘中,不退出
         ///   </summary>
         private   void  MainForm_FormClosing( object  sender, FormClosingEventArgs e)
        {
             // 如果我们操作【×】按钮,那么不关闭程序而是缩小化到托盘,并提示用户.
             if  ( this .WindowState  !=  FormWindowState.Minimized)
            {
                e.Cancel  =   true ; // 不关闭程序

                 // 最小化到托盘的时候显示图标提示信息,提示用户并未关闭程序
                 this .WindowState  =  FormWindowState.Minimized;
                notifyIcon1.ShowBalloonTip( 3000 ,  " 程序最小化提示 " ,
                      " 图标已经缩小到托盘,打开窗口请双击图标即可。 " ,
                     ToolTipIcon.Info);
            }
        }

         private   void  MainForm_Move( object  sender, EventArgs e)
        {
             if  ( this   ==   null )
            {
                 return ;
            }

             // 最小化到托盘的时候显示图标提示信息
             if  ( this .WindowState  ==  FormWindowState.Minimized)
            {
                 this .Hide();
                notifyIcon1.ShowBalloonTip( 3000 ,  " 程序最小化提示 " ,
                     " 图标已经缩小到托盘,打开窗口请双击图标即可。 " ,
                    ToolTipIcon.Info);
            }
        }

 2、只允许允许一个程序实例,即使是通过虚拟桌面方式连接过来的,也是只允许一个人运行。

 这个已经封装好代码了,只需要在Main函数里面调用一下函数即可,允许多个实例会出现下面的对话框提示信息,提示不允许多实例运行,如下所示:

 

代码如下所示。

         ///   <summary>
         ///  应用程序的主入口点。
         ///   </summary>
        [STAThread]
         private   static   void  Main()
        {
            GlobalMutex();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault( false );

             // ******启动代码**********
        }

         private   static  Mutex mutex  =   null ;
         private   static   void  GlobalMutex()
        {
             //  是否第一次创建mutex
             bool  newMutexCreated  =   false ;
             string  mutexName  =   " Global\\ "   +   " WareHouseMis " ;//系统名称,Global为全局,表示即使通过通过虚拟桌面连接过来,也只是允许运行一次
             try
            {
                mutex  =   new  Mutex( false , mutexName,  out  newMutexCreated);
            }
             catch  (Exception ex)
            {
                Console.Write(ex.Message);
                System.Threading.Thread.Sleep( 1000 );
                Environment.Exit( 1 );
            }

             //  第一次创建mutex
             if  (newMutexCreated)
            {
                Console.WriteLine( " 程序已启动 " );
                 // todo:此处为要执行的任务
            }
             else
            {
                MessageUtil.ShowTips( " 另一个窗口已在运行,不能重复运行。 " );
                System.Threading.Thread.Sleep( 1000 );
                Environment.Exit( 1 ); // 退出程序
            }

        } 

3、使用NotifyWindow给用户提示信息

 可以通过NotifyWindow类(最后附件中有),做一些信息的提示,方便用户了解一些重要信息的提示,界面较为友好,如下所示:

 

 提示信息的代码使用如下:

         ///   <summary>
         ///  弹出提示消息窗口
         ///   </summary>
         public   void  Notify( string  caption,  string  content)
        {
            Notify(caption, content,  400 ,  200 ,  5000 );
        }

         ///   <summary>
         ///  弹出提示消息窗口
         ///   </summary>
         public   void  Notify( string  caption,  string  content,  int  width,  int  height,  int  waitTime)
        {
            NotifyWindow notifyWindow  =   new  NotifyWindow(caption, content);
            notifyWindow.TitleClicked  +=   new  System.EventHandler(notifyWindowClick);
            notifyWindow.TextClicked  +=   new  EventHandler(notifyWindowClick);
            notifyWindow.SetDimensions(width, height);
            notifyWindow.WaitTime  =  waitTime;
            notifyWindow.Notify();
        }

         private   void  notifyWindowClick( object  sender, EventArgs e)
        {
             // SystemMessageInfo info = BLLFactory<SystemMessage>.Instance.FindLast();
             // if (info != null)
             // {
             //      // FrmEditMessage dlg = new FrmEditMessage();
             //      // dlg.ID = info.ID;
             //      // dlg.ShowDialog();
             // }
        } 

4、使用SearchCondion控件,简化查询条件的转化

不管在Winform或者在WebForm中,查询构造条件总是非常繁琐的事情,使用该控件能有效简化代码,提高操作的准确及方便行,这个控件我完成了几年了,一直伴随我处理各种查询操作。

         private   string  GetConditionSql()
        {
            SearchCondition condition  =   new  SearchCondition();
            condition.AddCondition( " ItemName " ,  this .txtName.Text, SqlOperator.Like)
                .AddCondition( " ItemBigType " ,  this .txtBigType.Text, SqlOperator.Like)
                .AddCondition( " ItemType " ,  this .txtItemType.Text, SqlOperator.Like)
                .AddCondition( " Specification " ,  this .cmbSpecNumber.Text, SqlOperator.Like)
                .AddCondition( " MapNo " ,  this .txtMapNo.Text, SqlOperator.Like)
                .AddCondition( " Material " ,  this .txtMaterial.Text, SqlOperator.Like)
                .AddCondition( " Source " ,  this .txtSource.Text, SqlOperator.Like)
                .AddCondition( " Note " ,  this .txtNote.Text, SqlOperator.Like)
                .AddCondition( " Manufacture " ,  this .txtManufacture.Text, SqlOperator.Like)
                .AddCondition( " ItemNo " ,  this .txtItemNo.Text, SqlOperator.LikeStartAt);
             string   where   =  condition.BuildConditionSql().Replace( " Where " ,  "" );

             return   where ;
        } 

可以构造条件后,传入查询函数,实现数据的查询。

             string   where   =  GetConditionSql();
            List < ItemDetailInfo >  list  =  BLLFactory < ItemDetail > .Instance.Find( where ,  this .winGridViewPager1.PagerInfo);
             this .winGridViewPager1.DataSource  =   new  WHC.Pager.WinControl.SortableBindingList < ItemDetailInfo > (list);
             this .winGridViewPager1.PrintTitle  =  Portal.gc.gAppUnit  +   "  --  "   +   " 备件信息报表 " ;  


最后呈上代码用到的一些类库及控件: https://files.cnblogs.com/wuhuacong/WinformTips.rar    

最近做了一个备件仓库管理软件,虽然只是一个不太复杂的仓库管理业务的软件,附带产出一些相关的报表,而且有之前做过的送水管理系统、酒店管理系统等软件的基础及技术储备,不过做起来发现还是有很多细节及新的东西,在客户不断提出修改意见以及改进建议的同时,逐步吸收优化新的知识,现大概总结一些相关的开发心得,以飨读者。

首先来看看整个软件的主体界面,如下所示,软件的功能主要集备件信息管理、备件入库、备件出库、库存查询、库房管理、业务报表、权限管理、数据字典管理、备件及库存导入等功能于一体。



 
现大致就主体界面介绍一些相关的知识:

1、使用OutLook样式的工具栏,提高界面的美观及易用性,这个在我的早期文章中已经有介绍,该控件集成美观的图标,可能很好提高界面的友好性及可操作性,可以令界面增色不少,详细请查看《 WinForm界面开发之“OutLookBar”工具条 》 在此不再赘述,只是介绍下并略为带过即可。

 

2、使用“WeifenLuo.WinFormsUI.Docking”控件来设计多文档界面效果,可以方便多个界面进行操作,这个控件也有相关的文章进行介绍使用了,请参考《 WinForm界面开发之布局控件"WeifenLuo.WinFormsUI.Docking"的使用 》 和《 再谈布局控件"WeifenLuo.WinFormsUI.Docking"的使用--如何控制自动停靠窗口的大小 》。

 

3、 尽可能使用已经在共享软件中广泛性使用我自己的分页控件,该分页控件集成了数据分页、内容提示、数据打印、数据导出、表头中文转义等很多功能,由于集成性很好,省却很多功夫,专注客户的业务及变化即可,否则一项表头的中文转换就够呛,还不说数据的分页,由于整合性、一致性、稳定性等特点,客户使用感觉比较好。最新的版本整合了优秀的Aspose.Cell控件来进行Excel数据的导出,速度非常快,而且默认表头冻结,非常方便。

 

4、 使用Apose.Cell控件的强大功能,实现自定义模板报表的定制导出。

使用普通的二维表,虽然能满足大多数的情况,不过在一般的业务中,自定义模板的报表根据贴近实际,符合客户的要求,虽然自定义模板的报表,比普通的二维报表复杂一些,不过利用Apose.Cell控件,并在预设模板中预设变量,可以生成很复杂的报表。详细历程可以参考我介绍Apose.Cell控件的使用文章,《 使用Aspose.Cell控件实现Excel高难度报表的生成(一) 》、《 使用Aspose.Cell控件实现Excel高难度报表的生成(二) 》。利用Apose.Cell可以生成下面几类自定义模板的报表:

   

 其他设计模板如下所示:

 
实际生成的报表如下所示:

 

5、利用现成独立的数据字典模块代码。由于一般复杂一点的系统,都需要有数据字典的模块,由于我在较早已经在这块已经做了一些开发,因此直接拿过来使用即可,该数据字典模块功能相对比较独立,因此是一个非常使用的模块,数据通过字典排序可以实现合理的排序,支持无限多级字典分类。

 

 实际使用的时候,也是非常方便,首先我们封装一下获取字典项目的方法如下所示:

           ///   <summary>

         ///  根据字典类型获取对应的CListItem集合
         ///   </summary>
         ///   <param name="dictTypeName"></param>
         ///   <returns></returns>
         public   static  CListItem[] GetDictByDictType( string  dictTypeName)
        {
            List < CListItem >  itemList  =   new  List < CListItem > ();
            Dictionary < string ,  string >  dict  =  BLLFactory < DictData > .Instance.GetDictByDictType(dictTypeName);
             foreach  ( string  key  in  dict.Keys)
            {
                itemList.Add( new  CListItem(key, dict[key]));
            }
             return  itemList.ToArray();
        }

 然后再窗体初始化的时候,添加字典的初始化代码即可,如下所示:

         private   void  InitDictItem()
        {
             this .txtManufacture.Items.Clear();
             this .txtManufacture.Items.AddRange(DictItemUtil.GetDictByDictType( " 供货商 " ));

             this .txtBigType.Items.Clear();
             this .txtBigType.Items.AddRange(DictItemUtil.GetDictByDictType( " 备件属类 " ));

             this .txtItemType.Items.Clear();
             this .txtItemType.Items.AddRange(DictItemUtil.GetDictByDictType( " 备件类别 " ));

             this .txtUnit.Items.Clear();
             this .txtUnit.Items.AddRange(DictItemUtil.GetDictByDictType( " 单位 " ));

             this .txtSource.Items.Clear();
             this .txtSource.Items.AddRange(DictItemUtil.GetDictByDictType( " 来源 " ));

             this .txtUsagePos.Items.Clear();
             this .txtUsagePos.Items.AddRange(DictItemUtil.GetDictByDictType( " 使用位置 " ));
             this .txtUsagePos.SelectedIndex  =   0 ;

             this .txtBelongDept.Items.Clear();
             this .txtBelongDept.Items.AddRange(DictItemUtil.GetDictByDictType( " 部门 " ));

             this .txtBelongWareHouse.Items.Clear();
             this .txtBelongWareHouse.Items.AddRange(Portal.gc.ManagedWareHouse.ToArray());
             this .txtBelongWareHouse.SelectedIndex  =   0 ;
       } 

通过数据字典大类无限级分类以及对字典内容的管理,基本上可以满足绝大多数的需要。

6、使用独立又具整合性的权限管理系统,既相互独立,有相互整合,方便重用,又不需重新开发,非常方便、更提高效率。 由于权限系统精简而又能满足日常绝大多数的需要,不会复杂的难于管理,而且也是基于角色的授权访问机制(RBAC),最重要是非常适合软件的整合使用。

 


用户角色功能维护界面如下:


编辑角色对应的权限界面如下:

如果我们在开发的系统中要集成现有的权限系统,操作代码如下所示:

         ///   <summary>
         ///  从数据库中列出相关用户
         ///   </summary>
         private   void  InitLoginName()
        {
            User userBLL  =   new  User();
            List < UserInfo >  userList  =  userBLL.GetAll();
             this .cmbzhanhao.Items.Clear();
             foreach  (UserInfo info  in  userList)
            {
                 this .cmbzhanhao.Items.Add(info.Name);
            }
        } 


登录的时候,只需要把该客户能操作的功能点放到内存列表中,如下所示:

                string  loginName  =   this .cmbzhanhao.Text.Trim();
                User userBLL  =   new  User();
                 string  identity  =  userBLL.VerifyUser(loginName,  this .tbPass.Text, Guid.NewGuid().ToString());
                 if  ( ! string .IsNullOrEmpty(identity))
                {
                    UserInfo info  =  userBLL.GetUserByName(loginName);

                     #region  获取用户的功能列表

                    Function functionBLL  =   new  Function();
                    List < FunctionInfo >  list  =  functionBLL.GetFunctionsByUser(info.ID,  " WareMis " );
                     if  (list  !=   null   &&  list.Count  >   0 )
                    {
                         foreach  (FunctionInfo functionInfo  in  list)
                        {
                             if  ( ! Portal.gc.FunctionDict.ContainsKey(functionInfo.ControlID))
                            {
                                Portal.gc.FunctionDict.Add(functionInfo.ControlID, functionInfo);
                            }
                        }
                    }

                     #endregion
               } 


 用户对某个功能点授权判断,只需要判断某功能点是否在已有集合中即可,如下所示:

           ///   <summary>

         ///  根据权限屏蔽功能
         ///   </summary>
         private   void  InitAuthorizedUI()
        {
             this .tool_Report.Enabled  =  Portal.gc.HasFunction( " Report " );
             this .tool_Dict.Enabled =  Portal.gc.HasFunction( " Dictionary " );
             this .tool_ItemDetail.Enabled  =  Portal.gc.HasFunction( " ItemDetail " );
             this .tool_Purchase.Enabled  =  Portal.gc.HasFunction( " Purchase " );
             this .tool_StockSearch.Enabled  =  Portal.gc.HasFunction( " StockSearch " );
             this .tool_TakeOut.Enabled  =  Portal.gc.HasFunction( " TakeOut " );

             this .menu_WareHouse.Enabled  =  Portal.gc.HasFunction( " WareHouse " );
             this .menu_Dictionary.Enabled  =  Portal.gc.HasFunction( " Dictionary " );
             this .menu_run_systemLog.Enabled  =  Portal.gc.HasFunction( " LoginLog " );
             this .menu_Parameters.Enabled  =  Portal.gc.HasFunction( " Parameters " );
             this .menu_MonthlyStatistic.Enabled  =  Portal.gc.HasFunction( " MonthlyStatistic " );
             this .menu_AnnualStatistic.Enabled  =  Portal.gc.HasFunction( " AnnualStatistic " );
             this .menu_ClearAll.Enabled  =  Portal.gc.HasFunction( " ClearAllData " );
             this .menu_ImportItemDetail.Enabled  =  Portal.gc.HasFunction( " ImportItemDetail " );
        }

因此,文章到这里先小结一下,就是利用现有成熟、稳定、集成性好的控件或者模块,或者利用合适易用的控件,既能事半功倍的完成任务,又能快速响应客户的需求变化 ,还能在界面整体上给客户留下好的印象,一举三得,何乐不为? 欢迎与大家一起探讨Winform开发的点点滴滴或者相互合作交流。 

主要研究技术:代码生成工具、Visio二次开发、送水管理软件等共享软件开发
    
  转载请注明出处:
撰写人:伍华聪   http: // www.iqidi.com  

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于总结的一些经验的详细内容...

  阅读:44次