【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能

摘要:
WinForm不像Web那样,可以在左侧指定右侧的链接,然后在右侧显示链接的页面。我自己的理解是这样的:上面Controls文件夹中的控件是在开发环境中已经定义好的,可以在控制台中打开编辑的,而这里的控件是在运行时生成的,就像Web开发中在后台拼Html语句,哦,这个比喻不太恰当,因为这里是面向对象的,都是些类了,但就是这个意思了。例如这里的PriorityMenu、DateFilterMenu、BackstageViewLabel、ContactToolTipController这些类都是直接或者间接继承UserControl的。
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第1张
这几个月一直在用DevExpress做公司的一个工具,布局模仿DevExpress控件包里面的一个示例(如上图),用Dev的朋友们应该对这个很熟悉吧#^_^#,从里面学到了许多东西,控件的基本使用,以及一些好的设计理念,哈哈,也不知道算不算理念,反正对我自己挺有帮助的,这里就总结了下,对自己是个巩固,如果有不恰当的地方,请大家不要吝惜手中的砖头。

布局很清晰,主体分为三部分:
①上面的Ribbon,放一命令按钮以及导航组件或者查找条件等;
②左侧的NavBarControl,起导航作用,其中包括Mail、Calendar、Contacts、Feeds、Tasks五个分组(NavBarGroup);
③右侧的展示区,我们这里称为Module(称呼为单元或者组件皆可,我在这里都称呼为单元
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第2张
主页面看着不怎么复杂,但是在程序启动时,若把每个分组下对应的数据页面都加载,可以想象那会有多么多么的蜗牛。WinForm不像Web那样,可以在左侧指定右侧的链接,然后在右侧显示链接的页面。但我们可以利用反射来解决这个问题,这篇文章里就只讲这一个知识点,其他的在后面再说。
先介绍下解决方案的构成,整体结构如下图:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第3张
① Controls文件夹下放置我们定义的用户控件,以及一个BackstageViewLable:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第4张
② Dictionaries,顾名思义,字典,语言包,切换语言用的,暂不讲,因为我也没用到,没研究●﹏●
③ Forms文件夹下存放我们建立的窗体:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第5张
一般的窗体都以frm做前缀,特殊的是最后两个窗体,ssMain是启动窗体,wfMain是切换页面时的等待窗体
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第6张【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第7张
④ Modules文件夹下存放也是用户控件,但注意跟Controls中用户控件的区别,没有uc前缀,他们是被作为Module(就是上文所谓的单元)来使用的,在上面布局图里所谓的Module区中的Panel里展示,主要是展示数据用的,当然也可以他用,之所以跟普通的用户控件区别对待,因为是他们位置特殊,应该可以这样理解吧,有待商酌。。。
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第8张
⑤ Resources中存放图片资源或者其他
⑥ Data类下存放的是要操作的数据的实体类,不明白这些类为什么不放到Data文件夹下,而是定义这样一个“类”,查看方便?
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第9张 ------------->>>>>>>>> 【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第10张
⑦下面是两个比较重要的“类”了,Controls 和 Helpers:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第11张<<<<<--------------------------->>>>>【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第12张
根据名字就可以大致推测他们的区别了,左侧的Controls中存放的控件,右侧的Helper中存放的帮助类,实际情况也是这样,但我们可能会注意到上面已经有一个Controls文件夹了,这里为什么还要有这样一个Controls“类”呢?
我自己的理解是这样的:上面Controls文件夹中的控件是在开发环境中已经定义好的,可以在控制台中打开编辑的,而这里的控件是在运行时生成的,就像Web开发中在后台拼Html语句,哦,这个比喻不太恰当,因为这里是面向对象的,都是些类了,但就是这个意思了。例如这里的PriorityMenu、DateFilterMenu、BackstageViewLabel、ContactToolTipController这些类都是直接或者间接继承UserControl的。运行时示例图:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第13张 PriorityMenu
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第14张 DateFilterMenu
当然,还有一些其他的类,以Manager结尾的是对一系列控件之间的组合操作类(好拗口●︿●);BaseControl类是自定义的控件基类,上面Controls文件夹中有一部分用户控件是从这个累继承来的;BaseModule类是单元的父类,上面Modules文件夹中的用户控件都必须继承这个类,“为什么”在后面讲;剩下的用到的时候再讲,现在先跳过。。。
Helpers中的类里面存放的都是些静态类,就是这里的类都不能被实例化,如下:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第15张
⑧ Resources中存放一些枚举、静态变量、常数、字符串操作类(跟业务逻辑无关)、委托等全局信息:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第16张
打开后内容:
【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能第17张
⑨ 剩下的很好理解了,frmMain.cs,主页面了,就第一张图示效果;Program.cs,开始启动,进入主页面~~~~
---------------------------------------------华丽的分割线,进入正题------------------------------------------------------
public partial classfrmMain : RibbonForm {
        MailType currentMailType =MailType.Inbox;
        ModulesNavigator modulesNavigator;
        internalFilterColumnsManager FilterColumnManager;
        ZoomManager zoomManager;
        List<BarItem> AllowCustomizationMenuList = new List<BarItem>();
        publicfrmMain() {
            InitializeComponent();
            rpcSearch.Text =TagResources.SearchTools;
            InitNavBarGroups();//初始化NarBarGroups,主要是给左侧的Group们的Tag绑定要在右侧Module中加载的页面
            SkinHelper.InitSkinGallery(rgbiSkins);//初始化主题,在rgbiSkins中加载主题列表
            RibbonButtonsInitialize();//初始化Ribbon中的控件们
           modulesNavigator = new ModulesNavigator(ribbonControl1, pcMain);//初始化单元导航组件(ModulesNavigator)
            zoomManager = new ZoomManager(ribbonControl1, modulesNavigator, beiZoom);//初始化伸缩管理组件
            modulesNavigator.ChangeGroup(navBarControl1.ActiveGroup, this);//上面的InitNavBarGroups初始化了左侧的Group,现在切换到第一个分组
            NavigationInitialize();//初始化导航控件
            SetPageLayoutStyle();//设置页面的布局样式
}

  ----上面是主页面的构造函数,有个印象就好,这里要讲的是切换分组时要用到反射的情况,下面也是此类中的一些关键方法,只讲重点----
       voidInitNavBarGroups() {
            nbgMail.Tag = new NavBarGroupTagObject("Mail", typeof(DevExpress.MailClient.Win.Mail));
            nbgCalendar.Tag = new NavBarGroupTagObject("Calendar", typeof(DevExpress.MailClient.Win.Calendar));
            nbgContacts.Tag = new NavBarGroupTagObject("Contacts", typeof(DevExpress.MailClient.Win.Contacts));
            nbgFeeds.Tag = new NavBarGroupTagObject("Feeds", typeof(DevExpress.MailClient.Win.Feeds));
            nbgTasks.Tag = new NavBarGroupTagObject("Tasks", typeof(DevExpress.MailClient.Win.Tasks));
        }
//切换左侧分组的操作,获取Group中应该加载的用户控件data,一般是列表之类的形式,连同分组e.Group作为参数传递过去,执行后续操作 
 private void navBarControl1_ActiveGroupChanged(objectsender, DevExpress.XtraNavBar.NavBarGroupEventArgs e) {
            object data =GetModuleData((NavBarGroupTagObject)e.Group.Tag);
            modulesNavigator.ChangeGroup(e.Group, data);
        } 
   
        protected objectGetModuleData(NavBarGroupTagObject tag) {
            if(tag == null) return null;
            if (tag.ModuleType == typeof(DevExpress.MailClient.Win.Calendar)) returnucCalendar1;
            if(tag.ModuleType == typeof(DevExpress.MailClient.Win.Feeds)) returnnavBarControl2;
            if(tag.ModuleType == typeof(DevExpress.MailClient.Win.Tasks)) returnnbgTasks;
            return null;
        }
---------------------------------N多代码省略-------------------------------------
}
主页面构造函数中这三行代码至关重要
①先看第一行的InitNavBarGroups(),方法中的nbgMail、nbgCalendaer等就是左侧的Group,然后分别给他们的Tag绑定了一个自定义的对象NavBarGroupTagObject,这样就可以知道每个Group对应哪个Module了(Module即为单元,即为我们上面放于Modules文件夹下,没有加uc的那些用户控件)。嗯,我以前没写过WinForm,也几乎没用过Tag这个属性,刚开始用的时候,看别人的代码大部分都是往Tag里面添加数据,不管多大都放得下,什么都放,然后我也就滥用了。只到看到了这里的用法,才知道Tag也可以像Web里面的超链接一样来用,存放所谓的“地址”,如下面的定义:
public classNavBarGroupTagObject {
        string name;//单元名称
        Type moduleType;//单元类型
        BaseModule module;//单元,由父类定义
        public NavBarGroupTagObject(stringname, Type moduleType) {
            this.name =name;
            this.moduleType =moduleType;
            module = null;
        }
        public string Name { get { returnname; } }
        public Type ModuleType { get { returnmoduleType; } }
        publicBaseModule Module {
            get { returnmodule; }
            set { module =value; }
        }
    }
②modulesNavigator = new ModulesNavigator(ribbonControl1, pcMain); 初始化了一个ModulesNavigator对象,下面是ModulesNavigator的定义。我把它称呼为“单元导航组件”,怪怪的名字,这不是关键~~~~
    • 由于不管切换到那个分组,主页面总有一个单元,所以定义一个CurrentModule;
    • 导航,所以定义一个ribbon,即最上面的RibbonControl 控件;
    • 单元是动态加载到Panel中的,所以定义了一个panel,存放单元;
下面的类很关键啦,黄色的地方关键的关键,用到了反射,有注释,先自己看下啦:
//主框架,单元导航组件(包含有一个RibbonControl和一个PanelControl)
    public classModulesNavigator
    {
        /// <summary>
        ///当前组件中的单元,位于panel中
        /// </summary>
        publicBaseModule CurrentModule
        {
            get{
                if (panel.Controls.Count == 0) //这里需要判断一下,是因为初始状态下panel里面没有控件,是经后面反射动态加载进去的
                    return null;
                return panel.Controls[0] asBaseModule;
            }
        }
        RibbonControl ribbon;
        PanelControl panel;
        publicModulesNavigator(RibbonControl ribbon, PanelControl panel)
        {
            this.ribbon =ribbon;
            this.panel =panel;
        }
        /// <summary>
        ///切换NavBarControl中分组所执行的操作:①跟NavBarGroup对应的RibbonPage才可以显示②在Panel中加载Tag对象中的Module
        /// </summary>
        /// <param name="group">NavBarControl中切换到的分组</param>
        /// <param name="moduleData">只在第一次加载时用到,注意这里不是单元中数据,而是左侧Group中应该加载的用户控件</param>
        public void ChangeGroup(NavBarGroup group, objectmoduleData)
        {
            bool allowSetVisiblePage = true;
            NavBarGroupTagObject groupObject = group.Tag as NavBarGroupTagObject;//把Tag中绑定的对象取出来
            if (groupObject == null) return;//检查下group中tag属性有没有绑定对象,没有的话就不执行后面操作了

            #region 判断哪些RibbonPage需要显示,放到deferredPagesToShow序列中(延期显示的Page们)List<RibbonPage> deferredPagesToShow = new List<RibbonPage>();
            foreach (RibbonPage page inribbon.Pages)
            {
                if (!string.IsNullOrEmpty(string.Format("{0}", page.Tag)))
                {
                    bool isPageVisible =groupObject.Name.Equals(page.Tag);
                    //要使Ribbon中的Page显示,需要满足如下两个条件:
                    //①当前选中分组的Tag属性绑定的对象的name属性值跟Ribbon中page的Tag属性值相等
                    //②Ribbon中的page的Visible==True
                    if (isPageVisible != page.Visible &&isPageVisible)
                        deferredPagesToShow.Add(page);
                    elsepage.Visible =isPageVisible;
                }
                if (page.Visible &&allowSetVisiblePage)
                {
                    ribbon.SelectedPage = page;//跟group对应的page可以显示
                    allowSetVisiblePage = false;
                }
            }
            #endregion

            #region 第一次加载 (InitModule)
            bool firstShow = groupObject.Module == null;//是否是第一次Show,第一次加载时Module为空
            if(firstShow)
            {
                if (SplashScreenManager.Default == null)//启动画面管理:如果没有默认的,用wfMain做等待窗口
                    SplashScreenManager.ShowForm(ribbon.FindForm(), typeof(CN_Standards.ISPET.Win.Forms.wfMain), false, true);
ConstructorInfo constructorInfoObj = groupObject.ModuleType.GetConstructor(Type.EmptyTypes);//获得单位中没有参数的那个构造函数(一般都只有一个无参构造函数)
                if (constructorInfoObj != null)
                {
                    groupObject.Module = constructorInfoObj.Invoke(null) as BaseModule;//关键地方哦,用反射获得分组绑定对象中的单元
                    groupObject.Module.InitModule(ribbon, moduleData);//这里是初始化Module对应的那个Group中的用户控件,每个Module中初始化有差别
}
                if (SplashScreenManager.Default != null)//不为空,这里的moduleData为主页面,此方法为主页面构造函数中的那个ChangeGroup方法
{
                    Form frm = moduleData as Form;//可以把单元中的moduleData转换成Form,
                    if (frm != null)
                        SplashScreenManager.CloseForm(false, 2000, frm);//延迟2秒后关闭等待窗口,打开主页面
                    elseSplashScreenManager.CloseForm();
                }
            }
            #endregion

            #region 让deferredPagesToShow序列中的RibbonPage显示出来,并让第一个被选中
            foreach (RibbonPage page indeferredPagesToShow)
            {//让跟group对应的page集合都显示出来
                page.Visible = true;
            }
            foreach (RibbonPage page inribbon.Pages)
            {//选中第一个关联的page
                if(page.Visible)
                {
                    ribbon.SelectedPage =page;
                    break;
                }
            }
            #endregion

            #region 再次加载以及离开此分组 (HideModule、ShowModule)
            if (groupObject.Module != null)
            {//清空panel,再次把groupObject.Module填充到panel中
                if (panel.Controls.Count > 0)
                {
                    BaseModule currentModule = panel.Controls[0] asBaseModule;
                    if (currentModule != null)
                        currentModule.HideModule();//在各个Module中执行
}
panel.Controls.Clear();
                panel.Controls.Add(groupObject.Module);
                groupObject.Module.Dock = DockStyle.Fill;//填充满
                groupObject.Module.ShowModule(firstShow);//显示单元,firstShow可能等于true,也可能是false
}
            #endregion}
    }
③第三行代码就是上面类里面定义的方法了,切换分组的时候会加载分组“链接”的单元到panel中,但也是分三个过程的:
InitModule:初始化单元,第一次切换至分组时才执行,把单元实例化出来,然后弄进Panel里面;
ShowModule:显示单元,每次切换分组时都执行,删除上个分组的单元,加载新分组的单元;(就像web里面重绘框架里面的页面)
HideModule:隐藏单元,在另个分组ShowModule前执行,主要做些保存状态等操作,以便下次回到此分组时,可以恢复状态。(类似web里面的ViewState
groupObject.Module.InitModule(ribbon, moduleData);
引用上面类里的一行代码,可以看到InitModule是在Module中执行的,其他两个,ShowModule和HideModule也是在Module中执行的,为什么这样呢?因为这里只是一个框架,提供了一些共有的操作,具体操作还得具体到每一个单元中,找一个单元,瞧一瞧:
internaloverridevoid InitModule(IDXMenuManager manager, object data)
{
base.InitModule(manager, data);//执行父类的初始化操作,就是这些Module有那些相同操作。。有点废话
EditorHelper.InitPriorityComboBox(repositoryItemImageComboBox1);//初始化重要性设置下拉框
this.ribbon = manager as RibbonControl;
ucMailViewer1.SetMenuManager(manager);
ShowAboutRow();
}
internaloverridevoid ShowModule(bool firstShow) {
base.ShowModule(firstShow);
if(firstShow) {
filterCriteriaManager =new FilterCriteriaManager(gridView1);
filterCriteriaManager.AddBarItem(OwnerForm.ShowUnreadItem, gcIcon, "[Read] = 0");
filterCriteriaManager.AddBarItem(OwnerForm.ImportantItem, gcPriority, "[Priority] = 2");
filterCriteriaManager.AddBarItem(OwnerForm.HasAttachmentItem, gcAttachment, "[Attachment] = 1");
filterCriteriaManager.AddClearFilterButton(OwnerForm.ClearFilterItem);
SetPriorityMenu();
SetDateFilterMenu();
OwnerForm.FilterColumnManager.InitGridView(gridView1);
} else {
lockUpdateCurrentMessage =false;//解除锁定
FocusRow(focusedRowHandle);//不是首次加载,选中上次移开时焦点所在行

}
gridControl1.Focus();
}
internaloverridevoid HideModule() {
lockUpdateCurrentMessage =true;//锁定
focusedRowHandle = gridView1.FocusedRowHandle;//保存焦点所在行

}
操作挺复杂的,只看我们想看的,可以看到ShowModule分为初次加载和非初次加载,初次加载时一堆操作,非初次加载,只是恢复了上次的状态;HideModule就简单了,保存状态即可。不用加载主页面时加载太多数据,也不用每次切换分组都重复加载数据,提高了系统的速度,增加了用户体验,维护也方便了很多。
好了,就先总结这么多了,其他的再慢慢总结。。详细代码就看DevExpress的第一个WinForm示例了,就不提供链接了。

免责声明:文章转载自《【DevExpress】4、在WinForm里用反射模拟Web里面的超链接功能》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇JSON和list之间的转换springboot后端实现条件查询,要配合使用mybatis下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

winform项目笔记:

餐饮管理系统:三层架构模式。 Modal,bll,dal,UI,Common.   知识点:     using语句释放的是非托管的资源。托管的资源会有CLR自动释放。     DataGridView控件:      dataGridView1.AutoGenerateColumns = false; 把自动生成列关掉。   事件其实也是一个方法,想要调用...

WinForm下窗体权限设计

权限设计 笔者不才看了园子里面很多园友写关于权限设计这块内容,那么笔者也在添一笔。这个是笔者在上完软件工程课程后,上交的一篇笔者论文,这里分享给大家交流,当然笔者经验尚浅,若内容有误,请大家指点出来,若大家有什么更好的想法,请提出来共同学习。 一.引言 在软件开发中, 从操作系统到一个仅仅能够发布文章的网站,都要涉及到权限的管理。在Windows 操作...

关于WINFORM中输入法的设置

关于WINFORM中输入法的设置 (转) johnsuna(阿山NET)的专栏 开发中正好遇到这类问题,网络真好啊:)下面是正文收集,感谢作者的辛勤工作给我们带来的便利. 在WINFORM中我们经常遇到文本输入框中输入法被禁用或老是变全角输入法等问题,经查阅相关资料,现小结如下: (一)Control.ImeMode 属性:获取或设置控件的输入法编辑器...

C# WinForm窗体界面设置问题

设置方法:  一:Form对象  属性:  设计中的Name:窗体类的类名AcceptButton:窗口的确定按钮CancelButton:窗口按ESC的取消按钮  1.外观  Backcolor:背景颜色Forecolor:字体颜色backgroundImage:背景图片Font:设置字体Formborderstyle:边框样式,常用Fixedsingl...

winform 中 MessageBox 用法大全

(转自:http://blog.csdn.net/xuenzhen123/article/details/4808005)  MessageBox.Show()共有21中重载方法。现将其常见用法总结如下:   1.MessageBox.Show(“Hello~~~~”); 最简单的,只显示提示信息。 2.MessageBox.Show(“There ar...

【WinForm程序】注册热键快捷键切换

重写DefWndProc事件 #region Window 消息捕获 const int WM_COPYDATA = 0x004A; public structCOPYDATASTRUCT { publicIntPtr dwData; public i...