Winform开发框架之客户关系管理系统(CRM)的开发总结系列4-Tab控件页面的动态加载

简介:

在前面介绍的几篇关于CRM系统的开发随笔中,里面都整合了多个页面的功能,包括多文档界面,以及客户相关信息的页面展示,这个模块就是利用DevExpress控件的XtraTabPage控件的动态加载实现的,本篇文章主要介绍两种方式的动态加载,一个是对用户控件(UserControl)模块的动态加载,一个是对普通窗体(Form)的动态加载,通过这两种方式,我们有时候可以动态实现很丰富的界面效果。

1、用户控件(UserControl)模块在Tab控件中的动态加载

参考了很多CRM的系统,一般都是把CRM系统中客户相关的信息放到下边,这也是成为了一种通用的界面布局,由于客户相关的信息很多,我们可以通过多文档方式放在下面的右下角的布局面板里面,由于整个程序是一个MDI多文档界面,不可以在子窗体里面实现另外一个MDI多文档的呈现,但是我们可以另辟蹊径,把它们放在一个TabPage页面里面,这样我们通过动态加载,也能实现我们需要的界面布局了。

由于我们想动态加载客户相关信息模块,因此每个模块可以通过用户控件的方式独立创建和维护,我们在项目工程里面创建相关的用户控件,如下所示。

我们为了方便更好的控制,我们需要定义一个所有用户控件的基类(BasePageControl)方便处理,该类定义了一些常规的数据和接口,代码如下所示。

    public partial class BasePageControl : XtraUserControl, IPageApply
    {
        /// <summary>
        /// 控件的客户ID
        /// </summary>
        public string CustomerID { get; set; }

        /// <summary>
        /// 登陆用户基础信息
        /// </summary>
        public LoginUserInfo LoginUserInfo { get; set; }
...............
    /// <summary>
    /// 用作页面的应用接口
    /// </summary>
    public interface IPageApply
    {
        /// <summary>
        /// 初始化页面的相关用户权限信息
        /// </summary>
        /// <param name="userInfo">用户信息</param>
        /// <param name="functionDict">权限信息</param>
        void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict);

        /// <summary>
        /// 根据客户ID属性绑定数据
        /// </summary>
        /// <param name="customerID">客户ID</param>
        void BindData(string customerID);
    }

设计好基类,然后设计相关模块的界面,继承自BasePageControl即可,该模块利用了我的Winform分页控件实现数据的展示,并在顶端设计了一个工具栏Bar。

以上的界面和普通的列表界面差不多,只是没有查询条件而已,所以可以利用代码生成工具Database2Sharp生成一个标准的列表页面,然后把大部分的代码复制过来进行适当的调整即可。

    public partial class ActivityControl : BasePageControl
    {
        public ActivityControl()
        {
            InitializeComponent();
            InitDictItem();

            this.winGridViewPager1.OnPageChanged += new EventHandler(winGridViewPager1_OnPageChanged);
            this.winGridViewPager1.OnStartExport += new EventHandler(winGridViewPager1_OnStartExport);
            this.winGridViewPager1.OnEditSelected += new EventHandler(winGridViewPager1_OnEditSelected);
            this.winGridViewPager1.OnAddNew += new EventHandler(winGridViewPager1_OnAddNew);
            this.winGridViewPager1.OnDeleteSelected += new EventHandler(winGridViewPager1_OnDeleteSelected);
            this.winGridViewPager1.OnRefresh += new EventHandler(winGridViewPager1_OnRefresh);
            this.winGridViewPager1.AppendedMenu = this.contextMenuStrip1;
            this.winGridViewPager1.ShowLineNumber = true;
            this.winGridViewPager1.BestFitColumnWith = false;//是否设置为自动调整宽度,false为不设置
            this.winGridViewPager1.gridView1.DataSourceChanged += new EventHandler(gridView1_DataSourceChanged);
            this.winGridViewPager1.gridView1.CustomColumnDisplayText += new DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventHandler(gridView1_CustomColumnDisplayText);
        }

.............................

设计好相关的客户相关资料模块后,我们就需要在主界面(上图)中加载相关的内容了,由于前面博客随笔说到,需要根据用户的配置选择进行动态加载,因此,我们需要先获取客户的选择列表,然后在根据列表进行判断是否加载显示。

            userPageList = BLLFactory<UserTreeSetting>.Instance.GetTreeSetting(pageCategory, LoginUserInfo.ID.ToString());

然后在获取标准的Tab选项卡配置列表,代码如下所示。

            List<SystemTreeNodeInfo> propList = BLLFactory<SystemTree>.Instance.GetTree(pageCategory);

接着定义一个函数,看标准的页面列表是否包含在用户配置列表里面。

        /// <summary>
        /// 如果列表为空或包含指定ID,则认为包含
        /// </summary>
        /// <param name="id">页面ID节点</param>
        /// <returns></returns>
        private bool ContainPage(string id)
        {
            bool result = false;
            if (userPageList == null || userPageList.Count == 0 || userPageList.Contains(id))
            {
                result = true;
            }
            return result;
        }

然后就是,根据相关信息,动态创建相关的Page页面,然后添加到Tab选项卡控件里面去即可,期间为了方便对界面的控制显示,我们需要传入主窗体的用户身份信息。

                    XtraTabPage page = new XtraTabPage();
                    page.Name = nodeInfo.ID;
                    page.Text = nodeInfo.TreeName;
                    page.ImageIndex = i % 20;//设置图标,总图标只有21个

                    BasePageControl control = CreateCustomerControl(nodeInfo.SpecialTag);
                    if (control != null)
                    {
                        control.InitFunction(LoginUserInfo, FunctionDict);//给子窗体赋值用户权限信息
                        control.Dock = DockStyle.Fill;
                        page.Controls.Add(control);
                    }
                    
                    this.tabCustomerRelated.TabPages.Add(page);

创建对象的函数CreateCustomerControl函数实现如下所示,通过上面的代码,我们就能动态创建相关的页面并显示出来了。

        /// <summary>
        /// 通过数据库配置的控件名称,反射创建对象
        /// </summary>
        /// <param name="controlName">控件名称(简称)</param>
        /// <returns></returns>
        private BasePageControl CreateCustomerControl(string controlName)
        {
            string namePrefix = "WHC.CRM.UI.CustomerPage.";
            controlName = namePrefix + controlName;//控件名称全称
            BasePageControl userControl = null;
            try
            {
                userControl = CreateInstance(controlName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(ex);
            }
            return userControl;
        }

        /// <summary>
        /// 根据全名和路径构造对象
        /// </summary>
        /// <param name="sName">对象全名</param>
        /// <param name="sFilePath">程序集路径</param>
        /// <returns></returns>
        private static BasePageControl CreateInstance(string sName, string sFilePath)
        {
            Assembly assemblyObj = Assembly.Load(sFilePath);
            if (assemblyObj == null)
            {
                throw new ArgumentNullException("sFilePath", string.Format("无法加载sFilePath={0} 的程序集", sFilePath));
            }

            BasePageControl obj = (BasePageControl)assemblyObj.CreateInstance(sName); //反射创建 
            return obj;
        }

最后提一下,我们创建的TabPage页面,虽然可以加上关闭图标,但是默认是关闭不了页面的,需要我们特殊处理,也就是需要在后台代码移除选定的选项卡页面,另外,为了考虑如果客户关闭了,我们可以保存它的设置,认为它在配置中隐藏显示这个页面。具体的代码逻辑如下所示。

        private void tabCustomerRelated_CloseButtonClick(object sender, EventArgs e)
        {
            //关闭移除
            this.tabCustomerRelated.TabPages.RemoveAt(this.tabCustomerRelated.SelectedTabPageIndex);
   
            List<string> nodeIdList = new List<string>();
            foreach (XtraTabPage page in this.tabCustomerRelated.TabPages)
            {
                //Tree ID= Page.Name
                if(!string.IsNullOrEmpty(page.Name));
                {
                    nodeIdList.Add(page.Name);
                }
            }

            bool result = BLLFactory<UserTreeSetting>.Instance.SaveTreeSetting(pageCategory, LoginUserInfo.ID.ToString(), nodeIdList);
        }

2、普通窗体(Form)在Tab控件中的动态加载

第一小节我们介绍了在TabControl控件里面动态加载多个用户控件(UserControl)页面,但是,由于我的CRM系统模块会涉及很多相关数据处理的界面的,如果把界面全部封装成用户控件,使用起来可能会麻烦一些。

如有时候在启动界面(Starter)模块里面,我们可能在菜单配置一个独立的模块放到一级菜单里面,如果是普通的列表窗口界面,那么创建多文档的子窗口就非常方便,如果是用户控件,就调用不了;因此我希望能够尽可能保留普通窗口的类型,而不是把它改为用户控件类型。

在上图中,我们看到,每个管理模块里面,还需要展示多个子模块页面,如果利用前面小节说到的用户控件方式,在TabControl控件加载肯定完全没有问题,本小节继续探寻是否可以不改变窗口类型的情况下,动态加载普通窗口界面内容到TabControl控件里面。

首先我们设计一个模块管理的主界面,包含了列表和TabControl的控件样式。

然后再找后台代码实现TabControl控件动态加载普通窗口对象,创建窗口对象前,我们需要判断是否该类型已经在TabControl里面存在了,如果存在者前置选项卡页面即可,主要的加载代码如下所示。

        /// <summary>
        /// 加载或者激活指定类型的对话框
        /// </summary>
        /// <param name="tabcontrol">XtraTabControl控件</param>
        /// <param name="formType">窗体类型,必须继承自BaseForm类型</param>
        private void LoadTabPageForm(XtraTabControl tabcontrol, Type formType, int imageIndex)
        {
            bool found = false;
            XtraTabPage selectedPage = null;
            foreach (XtraTabPage page in tabcontrol.TabPages)
            {
                if (page.Tag != null && page.Tag.ToString() == formType.Name)
                {
                    found = true;
                    selectedPage = page;
                    break;
                }
            }

            if (!found)
            {
                selectedPage = new XtraTabPage();
                BaseDock dlg = (BaseDock)Activator.CreateInstance(formType);
                dlg.Visible = true;
                dlg.Dock = DockStyle.Fill;
                dlg.FormBorderStyle = FormBorderStyle.None;
                dlg.TopLevel = false;//在这里一定要注意 

                dlg.InitFunction(LoginUserInfo, FunctionDict);//给子窗体赋值用户权限信息
                selectedPage.Text = dlg.Text;
                selectedPage.ImageIndex = imageIndex;
                selectedPage.Tag = dlg.GetType().Name;
                selectedPage.Controls.Add(dlg);
                tabcontrol.TabPages.Add(selectedPage);
            }

            selectedPage.BringToFront();
            tabcontrol.SelectedTabPage = selectedPage;
        }

实现上面的函数后,我们只需要在按钮事件里面,调用上面的函数,并传入相关的参数即可。

        private void itemProduct_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
        {
            LoadTabPageForm(this.xtraTabMain, typeof(FrmProduct), 0);
        }

        private void itemManufacturer_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
        {
            LoadTabPageForm(this.xtraTabMain, typeof(FrmManufacturer), 1);
        }

        private void itemCompetitor_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
        {
            LoadTabPageForm(this.xtraTabMain, typeof(FrmCompetitor), 2);
        }

同时关闭事件我们需要增加代码进行处理,关闭事件的代码如下所示。

        private void xtraTabMain_CloseButtonClick(object sender, EventArgs e)
        {
            XtraTabPage currentPage = this.xtraTabMain.SelectedTabPage;
            BaseForm form = currentPage.Controls[0] as BaseForm;
            if (form != null)
            {
                form.Close();
                form.Dispose();
            }

            this.xtraTabMain.TabPages.RemoveAt(this.xtraTabMain.SelectedTabPageIndex);
        }

以上就是在TabControl控件里面,动态加载用户控件、普通窗口的两种不同的方式,都能为我们实现丰富的界面布局展现,希望对大家在开发Winform界面效果上有所参考。

我的该CRM系统系列的几篇随笔链接如下,供阅读。

Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示 

Winform开发框架之客户关系管理系统(CRM)的开发总结系列2-基于框架的开发过程 

Winform开发框架之客户关系管理系统(CRM)的开发总结系列3-客户分类和配置管理实现 

Winform开发框架之客户关系管理系统(CRM)的开发总结系列4-Tab控件页面的动态加载 

本文转自博客园伍华聪的博客,原文链接:Winform开发框架之客户关系管理系统(CRM)的开发总结系列4-Tab控件页面的动态加载,如需转载请自行联系原博主。



目录
相关文章
|
7月前
|
XML JSON 运维
超兔CRM基于宜搭与多系统对接的归一结构分析与设计思考
本文探讨了超兔CRM团队在使用宜搭系统对接多外部系统过程中,面临的系统耦合高、接口冗余、维护成本上升等问题,提出通过引入中间表结构与业务对象模型抽象,实现接口归一化设计。该方案有效降低系统依赖,提升数据交互标准化程度,显著提高开发与维护效率,支撑业务长期扩展。
404 2
|
12月前
|
机器学习/深度学习 人工智能 大数据
销售易CRM:技术革新助力客户关系管理智能化
销售易CRM是国内领先的客户关系管理系统,通过人工智能、大数据、云计算和低代码开发等技术,助力企业实现智能化转型。其AI与机器学习功能可深度挖掘客户数据价值,提供精准商机洞察;大数据分析赋能企业决策智能化,提升预测能力和运营效率;基于云计算的移动办公功能打破协作边界,提高团队效率;低代码平台支持快速定制化开发,灵活响应业务需求。销售易CRM以技术创新和服务升级,推动企业在数字化浪潮中取得竞争优势,实现可持续发展。
|
12月前
|
数据可视化 搜索推荐 安全
点晴CRM:助力企业高效管理客户关系
在当今竞争激烈的商业环境中,客户关系管理(CRM)已成为企业制胜的关键。点晴CRM,作为一款专为企业打造的客户关系管理解决方案,致力于帮助企业高效管理客户关系,提升销售业绩,实现可持续增长。
163 0
|
8月前
|
前端开发 NoSQL 关系型数据库
如何开发CRM系统中的产品管理板块(附架构图+流程图+代码参考)
本文深入解析了CRM系统中产品管理模块的设计与实现,涵盖系统架构、业务流程、核心代码及落地建议,助力企业构建高效的产品管理体系。
|
8月前
|
数据可视化 数据挖掘 BI
如何开发CRM系统中的薪酬管理板块(附架构图+流程图+代码参考)
本文介绍如何在CRM系统中高效搭建薪酬管理模块,涵盖销售目标制定、绩效系数设置、薪酬计算及开发技巧,助力企业实现薪酬管理的数字化与自动化。
|
8月前
|
数据库 数据安全/隐私保护 UED
如何开发CRM系统中的订单管理板块(附架构图+流程图+代码参考)
CRM系统中的订单管理板块是企业数字化转型的关键工具,能够高效管理客户订单、提升销售效率和客户满意度。本文详解订单管理的功能设计、业务流程、开发技巧及代码实现,助力企业优化订单流程、提高业绩。
|
8月前
|
数据库
如何开发CRM系统中的财务管理板块(附架构图+流程图+代码参考)
客户关系管理(CRM)系统中的财务管理模块对企业运营至关重要。它不仅提升工作效率,还能优化销售流程、确保财务数据准确记录。本文详解如何在CRM中搭建财务管理板块,涵盖开票申请、回款单处理、业务流程设计、开发技巧及注意事项,助力企业打造高效财务管理解决方案。
|
8月前
|
存储 缓存 数据可视化
如何开发CRM系统中的客户管理板块(附架构图+流程图+代码参考)
本文深入解析CRM系统中客户管理模块的开发,涵盖功能设计、业务流程、技术实现及代码参考,助力开发者构建高效客户管理系统。
|
8月前
|
机器学习/深度学习 算法 API
如何开发CRM系统中的商机管理板块(附架构图+流程图+代码参考)
CRM系统中的商机管理板块是用于记录、跟踪和管理潜在销售机会的关键模块。它帮助企业捕捉客户需求,评估商机价值,并通过科学的流程推动商机转化,从而提高销售效率与业绩。本文将深入解析商机管理的核心功能、业务流程及开发实现,助力企业构建高效的CRM系统。
|
11月前
|
Java 数据库
jsp CRM客户管理系统(含数据库脚本以及文档)
jsp CRM客户管理系统(含数据库脚本以及文档)
253 10

热门文章

最新文章