在Winform开发框架中使用DevExpress的内置图标资源
在开发Winform程序界面的时候,我们往往会使用一些较好看的图表,以便能够为我们的程序界面增色,良好的图标设置可以让界面看起来更加美观舒服,而且也比较容易理解,图标我们可以通过一些网站获取各种场景的图标资源,不过本篇随笔主要介绍如何利用DevExpress的内置图标资源来实现界面图标的设置。
1、设计时刻的图标处理
丰富的图标处理,在菜单、工具栏、树列表等地方,以及按钮等地方,都可以使用,而这些我们可以利用DevExpress的内置图标选择来减轻我们寻找合适图标的烦恼。
一些按钮、工具栏等的图标设置,一般是固定的,我们往往可以在设计时刻就指定它,这样我们可以使用本地的图标,也可以使用DevExpress的内置图标。而使用DevExpress内置图标资源的时候,我们可以调出DevExpress的内置图标选择框的。
如下是按钮添加图标方式,操作非常简单,在按钮的右上角小图标上单击一下进入编辑界面,如下所示。
然后选择Image按钮,进入图标选择界面,选择内置的DevExpress图标库即可,基本上,只要是DevExpress的原生控件,那么就可以通过这种内置图标的对话框进行图标选择,非常方便。
2、运行时刻的图标处理
上面的操作是在设计时候,DevExpress设计器给我们提供很多便利选择内置图标,而在界面运行时刻,想动态处理界面按钮图标,或者树形菜单的图标的时候,就没有这个直接的接口来设置图标了,而我们框架的菜单往往都是需用动态增加的,因此图标的设置也是在运行时刻的。如下面的树列表中,图标就是动态指定的。
这些动态的树形菜单,是在权限系统里面动态配置的,菜单的配置界面如下所示。
上面的选择图图标就是我们需要动态设置的图标,由于图标资源我们是以图片形式存储在对应的记录里面的,因此使用起来也是比较方便的,我们在配置的时候,获取到对应的图标资源并存储起来即可。
除了上面可以参考从DevExpress内置图标资源获取图标的方式外
我们还可以选择我们自己喜欢的图标资源,也就是从系统图标文件中选择自己喜欢的,如下界面所示。
因此我考虑在运行时刻整合两种不同选择图标的方式。
我们先来看看我整合后的图表选择界面,如下所示,包含了运行时刻提取DevExpress内置图标的功能和从系统文件中选择图标的功能。
3、运行时刻提取DevExpress内置图标的功能实现
首先我们参考设计时刻的界面展示
来设计一个界面来展示图标信息
参考原版的界面,设计尽可能贴近即可,另外我们自己加入一个从系统选择图标资源的操作。
至于图标选中后我们返回对应的Image对象给调用者,则通过事件进行处理,以便选中后,即使更新显示效果。
如下所示,我们定义一个委托和事件。
- /// <summary>
- /// DevExpress图标和系统图标选择窗体
- /// </summary>
- public partial class FrmImageGallery : BaseForm
- {
- /// <summary>
- /// 自定义一个委托处理图标选择
- /// </summary>
- public delegate void IconSelectHandlerDelegate(Image image, string name);
- /// <summary>
- /// 图标选择的事件
- /// </summary>
- public event IconSelectHandlerDelegate OnIconSelected;
- private DXImageGalleryLoader loader = null;
- public FrmImageGallery()
- {
- InitializeComponent();
- InitDictItem();//初始化
- }
- /// <summary>
- /// 处理图标选择的事件触发
- /// </summary>
- public virtual void ProcessIconSelected(Image image, string name)
- {
- if (OnIconSelected != null)
- {
- OnIconSelected(image, name);
- }
- }
然后在内置图标显示中,如果触发图标的单击,我们就触发事件,以便让调用者更新界面显示,如下代码所示。
- foreach (GalleryItem item in items[key])
- {
- item.ItemClick += (s, e) =>
- {
- //选择处理
- ProcessIconSelected(item.ImageOptions.Image, item.Description);
- };
- }
而对于从系统文件加载文件进行显示图标的,类似的触发方式。
- /// <summary>
- /// 从系统资源中加载图标文件,然后触发事件进行显示
- /// </summary>
- private void txtFilePath_Properties_ButtonClick(object sender, ButtonPressedEventArgs e)
- {
- string file = GetIconPath();
- if (!string.IsNullOrEmpty(file))
- {
- this.txtFilePath.Text = file;//记录文件名
- this.txtEmbedIcon.Image = LoadIcon(file);//显示图片
- this.txtEmbedIcon.Size = new System.Drawing.Size(, );
- //返回处理
- ProcessIconSelected(this.txtEmbedIcon.Image, file);
- }
- }
这样我们在菜单的选择图标的时候,就可以触发事件进行获取图表并更新自身了。
- private void btnSelectIcon_Click(object sender, EventArgs e)
- {
- FrmImageGallery dlg = new FrmImageGallery();
- dlg.OnIconSelected += (image, name) =>
- {
- this.txtEmbedIcon.Image = image;
- };
- dlg.ShowDialog();
- }
完成了这些处理,我们再次将焦点放在如何提取并展示DevExpress内置图标的身上。
为了获取图表资源里面的分类及大小等信息,我们需要把图标资源进行一个加载出来,然后读取里面的类别和大小、集合等信息。先定义几个变量来承载这些信息。
- /// <summary>
- /// 图标分类
- /// </summary>
- public List<string> Categories { get; set; }
- /// <summary>
- /// 图标集合
- /// </summary>
- public List<string> Collection { get; set; }
- /// <summary>
- /// 图标尺寸
- /// </summary>
- public List<string> Size { get; set; }
我们知道,DevExpress的图标资源在程序集DevExpress.Utils.DxImageAssemblyUtil.ImageAssembly里面,因此我们需要对它进行读取,并依次对各个资源进行处理。
我们来看看具体的处理代码,如下所示。
- using (System.Resources.ResourceReader reader = GetResourceReader(DevExpress.Utils.DxImageAssemblyUtil.ImageAssembly))
- {
- System.Collections.IDictionaryEnumerator dict = reader.GetEnumerator();
- while (dict.MoveNext())
- {
- string key = (string)dict.Key as string;
- if (!DevExpress.Utils.DxImageAssemblyUtil.ImageProvider.IsBrowsable(key)) continue;
- if (key.EndsWith(".png", StringComparison.Ordinal))
- {
- string reg = @"(?<collection>\S*?)/(?<category>\S*?)/(?<name>\S*)";
- var collectionItem = CRegex.GetText(key, reg, "collection");
- var categoryItem = CRegex.GetText(key, reg, "category");
- string sizeReg = @"_(?<size>\S*)\.";
- var sizeItem = CRegex.GetText(key, sizeReg, "size");
- if (!this.Collection.Contains(collectionItem))
- {
- this.Collection.Add(collectionItem);
- }
- if (!this.Categories.Contains(categoryItem))
- {
- this.Categories.Add(categoryItem);
- }
- if (!this.Size.Contains(sizeItem))
- {
- this.Size.Add(sizeItem);
- }
- Image image = GetImageFromStream((System.IO.Stream)dict.Value);
- if (image != null)
- {
- var item = new DevExpress.XtraBars.Ribbon.GalleryItem(image, key, key);
- if (!ImageCollection.ContainsKey(key))
- {
- ImageCollection.Add(key, item);
- }
- }
- }
- }
- }
其中读取资源的操作代码是
- GetResourceReader(DevExpress.Utils.DxImageAssemblyUtil.ImageAssembly)
这个代码它就是从资源里面进行获取对应的图表资源。
- private System.Resources.ResourceReader GetResourceReader(System.Reflection.Assembly imagesAssembly)
- {
- var resources = imagesAssembly.GetManifestResourceNames();
- var imageResources = Array.FindAll(resources, resourceName => resourceName.EndsWith(".resources"));
- if (imageResources.Length != )
- {
- throw new Exception("读取异常");
- }
- return new System.Resources.ResourceReader(imagesAssembly.GetManifestResourceStream(imageResources[]));
- }
另外,我们根据图表的文件名结构,我们通过正则表达式来读取它的对应信息,然后把它的大小、类别、集合信息存储起来。
- string reg = @"(?<collection>\S*?)/(?<category>\S*?)/(?<name>\S*)";
- var collectionItem = CRegex.GetText(key, reg, "collection");
- var categoryItem = CRegex.GetText(key, reg, "category");
- string sizeReg = @"_(?<size>\S*)\.";
- var sizeItem = CRegex.GetText(key, sizeReg, "size");
图表信息读取了,我们需要解析它然后存储起来,把图标的Image对象放在一个字典类别里面,方便按照组别进行展示。
- Image image = GetImageFromStream((System.IO.Stream)dict.Value);
- if (image != null)
- {
- var item = new DevExpress.XtraBars.Ribbon.GalleryItem(image, key, key);
- if (!ImageCollection.ContainsKey(key))
- {
- ImageCollection.Add(key, item);
- }
- }
有了这些资源,我们对它们进行搜索就显得很方便了,我们如果需要根据文件名或者其他条件进行查询集合的数据,提供一个通用的方法即可,如下代码所示。
- /// <summary>
- /// 根据条件获取集合
- /// </summary>
- /// <returns></returns>
- public Dictionary<string, GalleryItemCollection> Search(List<string> collection, List<string> categories,
- List<string> size, string fileName = "")
- {
- Dictionary<string, GalleryItemCollection> dict = new Dictionary<string, GalleryItemCollection>();
- GalleryItemCollection list = new GalleryItemCollection();
- foreach (var key in ImageCollection.Keys)
- {
- //使用正则表达式获取图标文件名中的集合、类别、大小等信息
- string reg = @"(?<collection>\S*?)/(?<category>\S*?)/(?<name>\S*)";
- var collectionItem = CRegex.GetText(key, reg, "collection");
- var categoryItem = CRegex.GetText(key, reg, "category");
- string sizeReg = @"_(?<size>\S*)\.";
- var sizeItem = CRegex.GetText(key, sizeReg, "size");
- //如果是查询处理,把记录放到查询结果里面
- if (!string.IsNullOrEmpty(fileName))
- {
- if(key.Contains(fileName))
- {
- list.Add(ImageCollection[key]);
- }
- dict["查询结果"] = list;
- }
- else
- {
- //如果是集合和列表中包含的,把它们按类别添加到字典里面
- if (collection.Contains(collectionItem) &&
- categories.Contains(categoryItem) &&
- size.Contains(sizeItem))
- {
- if (!dict.ContainsKey(categoryItem))
- {
- GalleryItemCollection cateList = new GalleryItemCollection();
- cateList.Add(ImageCollection[key]);
- dict[categoryItem] = cateList;
- }
- else
- {
- GalleryItemCollection cateList = dict[categoryItem];
- cateList.Add(ImageCollection[key]);
- }
- }
- }
- }
- return dict;
- }
这次搜索就直接基于已有的集合ImageCollection 进行搜索的了,不用再次读取程序集并依次分析它,速度提供不少的。
由于图表资源的处理是比较耗时的,我们把整个图标加载的类作为一个静态的对象缓存起来,这样下次使用直接从缓存里面拿,对应的资源也不用重新加载,更好的提高我们重用的效果了,体验更好了。
- /// <summary>
- /// 图标库加载处理
- /// </summary>
- public class DXImageGalleryLoader
- {
- /// <summary>
- /// 图标字典类别集合
- /// </summary>
- public Dictionary<string, GalleryItem> ImageCollection { get; set; }
- /// <summary>
- /// 图标分类
- /// </summary>
- public List<string> Categories { get; set; }
- /// <summary>
- /// 图标集合
- /// </summary>
- public List<string> Collection { get; set; }
- /// <summary>
- /// 图标尺寸
- /// </summary>
- public List<string> Size { get; set; }
- /// <summary>
- /// 使用缓存处理,获得对象实例
- /// </summary>
- public static DXImageGalleryLoader Default
- {
- get
- {
- System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod();
- string keyName = string.Format("{0}-{1}", method.DeclaringType.FullName, method.Name);
- var result = MemoryCacheHelper.GetCacheItem<DXImageGalleryLoader>(keyName,
- delegate () { return new DXImageGalleryLoader().LoadData(); },
- new TimeSpan(, , ));//30分钟过期
- return result;
- }
- }
以上代码通过
- public static DXImageGalleryLoader Default
定义了一个静态的实例属性,这样这个 DXImageGalleryLoader 实例只会在程序第一次使用的时候构建并加载图片资源,后续都是从缓存里面读取,提高响应速度的同时,也会记住上次的选择界面内容。
以上就是整个功能的处理思路,以及一步步的优化处理,以便实现功能展示的同时,也提高响应速度,最终界面就是我们开始的时候介绍的那样。
单击或者选中系统图标后, 需要设置的按钮或者界面,就会及时更新图标展示,体验效果还是非常不错的。
由于这个界面功能的通用性,我把它作为系统界面基础模块,放到了我的框架BaseUIDx里面,各个系统模块都可以调用了。
在Winform开发框架中使用DevExpress的内置图标资源的更多相关文章
- 在Winform开发框架中使用DevExpress的TreeList和TreeListLookupEdit控件
DevExpress提供的树形列表控件TreeList和树形下拉列表控件TreeListLookupEdit都是非常强大的一个控件,它和我们传统Winform的TreeView控件使用上有所不同,我一 ...
- 在Winform开发框架中,利用DevExpress控件实现数据的快速录入和选择
在实际的项目开发过程中,有好的控件或者功能模块,我都是想办法尽可能集成到我的WInform开发框架中,这样后面开发项目起来,就可以节省很多研究时间,并能重复使用,非常高效方便.在我很早之前的一篇博客& ...
- Winform开发框架中工作流模块的业务表单开发
在我们开发工作流的时候,往往需要设计到具体业务表单信息的编辑,有些是采用动态编辑的,有些则是在开发过程中处理的,各有各的优点,动态编辑的则方便维护各种各样的表单,但是数据的绑定及处理则比较麻烦,而自定 ...
- Winform开发框架中的综合案例Demo
在实际的系统开发中,我们往往需要一些简单的的案例代码,基于此目的我把Winform开发框架中各种闪光点和不错的功能,有些是我们对功能模块的简单封装,而有些则是引入了一些应用广泛的开源组件进行集成使用, ...
- Winform开发框架中实现同时兼容多种数据库类型处理
在很多应用系统里面,虽然一般采用一种数据库运行,但是由于各种情况的需要,可能业务系统会部署在不同类型的数据库上,如果开发的系统能够很方便支持多种数据库的切换,那可以为我们减少很多烦恼,同时提高系统的适 ...
- Winform开发框架中工作流模块之申请单草稿处理
在我们开发工作流模块的时候,有时候填写申请单过程中,暂时不想提交审批,那么可以暂存为草稿,以供下次继续填写或者提交处理,那么这个草稿的功能是比较实用的,否则对于一些填写内容比较多的申请单,每次要重填写 ...
- 参照企业微信审批业务,在Winform开发框架中工作流模块的实现业务审批
目前微信的企业号已经切换到企业微信里面,这个是一个APP程序,提供了很丰富的企业应用,其中包括了业务审批处理,审批业务包括请假.报销.费用.出差等很多个审批场景,在Winform开发框架中工作流模块这 ...
- Winform开发框架中工作流模块之审批会签操作(2)
前面随笔介绍了请假申请单和报销申请单两个不同的业务表单的流程处理,一个是单表信息,一个包含明细的主从表信息,后者包含了条件流程的处理,在流程审批中,一般还有一种流程处理就是会签的操作,会签处理是几个审 ...
- Winform开发框架中工作流模块之审批会签操作
在前面介绍了框架中工作流的几个开发过程,本篇随笔重点介绍一下日常审批环节中的具体处理过程,从开始创建表单,以及各个审批.会签过程的流转过程,希望大家对其中流程的处理有一个大概的印象. 1.请假申请表单 ...
随机推荐
- 流式大数据计算实践(6)----Storm简介&使用&安装
一.前言 1.这一文开始进入Storm流式计算框架的学习 二.Storm简介 1.Storm与Hadoop的区别就是,Hadoop是一个离线执行的作业,执行完毕就结束了,而Storm是可以源源不断的接 ...
- Ruby数组方法整理
数组方法整理 方法列表: all().any().none()和one():测试数组中的所有或部分元素是否满足给定条件.条件可以是语句块中决定,也可以是参数决定 append():等价于push() ...
- ROS笔记 Topics
http://wiki.ros.org/ROS/Tutorials/UnderstandingTopics rostopic rqt_graph rosmsg rqt_graph 一个用于查看topi ...
- [Linux] scp本地服务器和远程服务器拷贝文件
上传本地文件到服务器scp 本地路径 用户名@远程服务器ip:远程路径 下载文件 scp 用户名@远程服务器ip:远程路径 本地路径-r 是上传下载本地目录到远程 远程文件
- Java开发笔记(二十七)数值包装类型
方法的出现缘起优化代码结构,但它的意义并不局限于此,正因为有了方法定义,编程语言才更像一门能解决实际问题的工具,而不仅仅是只能用于加减乘除的计算器.在数学的发展过程中,为了表示四则运算,人们创造了加减 ...
- Java开发笔记(三十一)字符类型的表达
前面介绍的Java编程,要么是与数字有关的计算,要么是与逻辑有关的推理,充其量只能实现计算器和状态机.若想让Java运用于更广阔的业务领域,就得使其支撑更加血肉丰满的业务场景,而丰满的前提是能够表达大 ...
- 【.Net Core】获取绝对路径、相对路径
一.绝对路径 1.获取应用程序运行当前目录Directory.GetCurrentDirectory(). System.IO命名空间中存在Directory类,提供了获取应用程序运行当前目录的静态方 ...
- Excel读写
http://www.cnblogs.com/mingforyou/archive/2013/08/26/3282922.html 读Excel代码如下: import java.io.File;im ...
- java SPI机制
1. SPI是Service Provider Interfaces的简称.根据Java的SPI规范,我们可以定义一个服务接口,具体的实现由对应的实现者去提供,即Service Provider(服务 ...
- USSD 杂记
Android Oreo允许应用程序读取来自运营商的USSD消息. 利用emoney执行话费充值,需要执行USSD代码,尝试编写apk执行ussd代码进行充值. 尝试在Android8的系统上进行US ...