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 == || userPageList.Contains(id))
{
result = true;
}
return result;
}
然后就是,根据相关信息,动态创建相关的Page页面,然后添加到Tab选项卡控件里面去即可,期间为了方便对界面的控制显示,我们需要传入主窗体的用户身份信息。
XtraTabPage page = new XtraTabPage();
page.Name = nodeInfo.ID;
page.Text = nodeInfo.TreeName;
page.ImageIndex = i % ;//设置图标,总图标只有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), );
} private void itemManufacturer_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
{
LoadTabPageForm(this.xtraTabMain, typeof(FrmManufacturer), );
} private void itemCompetitor_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
{
LoadTabPageForm(this.xtraTabMain, typeof(FrmCompetitor), );
}
同时关闭事件我们需要增加代码进行处理,关闭事件的代码如下所示。
private void xtraTabMain_CloseButtonClick(object sender, EventArgs e)
{
XtraTabPage currentPage = this.xtraTabMain.SelectedTabPage;
BaseForm form = currentPage.Controls[] 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控件页面的动态加载的更多相关文章
- Winform开发框架之客户关系管理系统(CRM)的开发总结系列3-客户分类和配置管理实现
我在本系列随笔的开始,介绍了CRM系统一个重要的客户分类的展示界面,其中包含了从字典中加载分类.从已有数据中加载分类.以及分组列表中加载分类等方式的实现,以及可以动态对这些节点进行配置,实现客户分类的 ...
- Winform开发框架之客户关系管理系统(CRM)的开发总结系列2-基于框架的开发过程
在上篇随笔<Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示>中介绍了我的整个CRM系统的概貌,本篇继续本系列的文章,介绍如何基于我的<winform ...
- Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示
一直以来,都希望整合一个以客户为中心的平台,有两个方面的考虑:一是实现客户数据.客户关系.客户管理等方面的整合,以便更好利用好客户的相关资源,发挥最大的营销效益:二是整合目前我的开发框架的所有模块和技 ...
- Winform开发框架之客户关系管理系统(CRM)的报价单和销售单的处理
在前面介绍了很多CRM相关的界面和实现思路的随笔文章,本篇继续介绍一下系统中用到的一些经验和技巧片段.本篇随笔主要介绍客户关系管理系统(CRM)的报价单和销售单的处理界面效果,使用列表内置的选择代替弹 ...
- 客户关系管理系统(CRM)的开发过程中使用到的开发工具总结
开发<客户关系管理系统(CRM)>软件过程,也就是一个标准的Winform程序的开发过程,我们可以通过这个典型的软件开发过程来了解目前的开发思路.开发理念,以及一些必要的高效率手段.本篇随 ...
- Java高级项目实战02:客户关系管理系统CRM系统模块分析与介绍
本文承接上一篇:Java高级项目实战之CRM系统01:CRM系统概念和分类.企业项目开发流程 先来CRM系统结构图: 每个模块作用介绍如下: 1.营销管理 营销机会管理:针对企业中客户的质询需求所建立 ...
- 客户关系管理系统CRM
http://www.cnblogs.com/Michael2397/tag/SSH%E9%A1%B9%E7%9B%AE-CRM/ 客户关系管理系统
- 会员管理系统的设计和开发(2)-- RDLC报表的设计及动态加载
在上篇<会员管理系统的设计和开发(1)>介绍了关于会员系统的一些总体设计思路和要点,经过一段时间开发,软件终于完成并发布.在这期间,碰到了不少技术难点,并积累了不少开发心得和经验,本篇继续 ...
- 客户关系管理系统-CRM源码
QQ:2112326142 邮箱:jxsupport@qq.com 本公司开发的CRM源代码系统一份,附源代码,本公司产品唯一销售客服QQ号:2112326142 请联系此QQ号,以免给您的工作 ...
随机推荐
- java生成解析xml的另外两种方法Xstream
Xstream生成和解析xm和JAXB生成和解析xml的方法. 一,Xstream Xstream非jdk自带的,需要到入Xstream-1.4.3.jar和xpp3_min-1.1.4.jar 1. ...
- dissmiss a UISearchBar with an SearchBarController
If you want do dissmiss a UISearchBar with an SearchBarController, just use this Code: [self.searchD ...
- Linux网络流量实时监控ifstat iftop命令详解
ifstat 介绍 ifstat工具是个网络接口监测工具,比较简单看网络流量 实例 默认使用 #ifstat eth0 eth1 KB /s i ...
- Android JNI 之 JNIEnv 解析
jni.h文件 : 了解 JNI 需要配合 jni.h 文件, jni.h 是 Google NDK 中的一个文件, 位置是 $/android-ndk-r9d/platforms/android-1 ...
- 三种ViewController跳转的异同
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag comple ...
- 关于STM8空间不足的解决方法
STM8虽然功能齐全,但是空间不足也是经常出来的情况.要么.text overflow,要么.bss overflow,让人头疼.这里把一些优化方案列出来,让空间得到充分利用: 1.在Project ...
- 开发BI系统时的需求分析研究
我们知道MIS,知道ERP,知道GIS等等,这些系统在管理限制上有很多的冲突,管理和被管理,开放和限制等等,然而BI在开始就不是这样的.BI要求的就是易用还要易于扩展,首先是报表,这个是你无条件的需要 ...
- 欲善其事必先利其器---Xcode插件
Xcode所有的插件都安装在目录~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/ Alcatraz (用于管理xcode插件 ...
- 正确理解静态Static关键字
与其他语言不通,C#没有全局变量和全局函数的感念,C#的所有字段和方法都出现在一个类的上下文中,在C#中与全局字段或函数等价的就是静态字段或方法.“全局字段或方法”与“C#的静态字段或方法”没有功能上 ...
- php分享三十三:用php中的register_shutdown_function和fastcgi_finish_request
已前言 在php中又两个方法都是在请求快结束的时候执行.方法名分别是 register_shutdown_function和fastcgi_finish_request.虽然执行的时机差不多,但是功能 ...