在開發Winform程序界面的時候,咱們每每會使用一些較好看的圖表,以便可以爲咱們的程序界面增色,良好的圖標設置可讓界面看起來更加美觀舒服,並且也比較容易理解,圖標咱們能夠經過一些網站獲取各類場景的圖標資源,不過本篇隨筆主要介紹如何利用DevExpress的內置圖標資源來實現界面圖標的設置。html
一、設計時刻的圖標處理
豐富的圖標處理,在菜單、工具欄、樹列表等地方,以及按鈕等地方,均可以使用,而這些咱們能夠利用DevExpress的內置圖標選擇來減輕咱們尋找合適圖標的煩惱。正則表達式
一些按鈕、工具欄等的圖標設置,通常是固定的,咱們每每能夠在設計時刻就指定它,這樣咱們可使用本地的圖標,也可使用DevExpress的內置圖標。而使用DevExpress內置圖標資源的時候,咱們能夠調出DevExpress的內置圖標選擇框的。緩存
以下是按鈕添加圖標方式,操做很是簡單,在按鈕的右上角小圖標上單擊一下進入編輯界面,以下所示。框架
而後選擇Image按鈕,進入圖標選擇界面,選擇內置的DevExpress圖標庫便可,基本上,只要是DevExpress的原生控件,那麼就能夠經過這種內置圖標的對話框進行圖標選擇,很是方便。ide
二、運行時刻的圖標處理
上面的操做是在設計時候,DevExpress設計器給咱們提供不少便利選擇內置圖標,而在界面運行時刻,想動態處理界面按鈕圖標,或者樹形菜單的圖標的時候,就沒有這個直接的接口來設置圖標了,而咱們框架的菜單每每都是需用動態增長的,所以圖標的設置也是在運行時刻的。以下面的樹列表中,圖標就是動態指定的。工具
這些動態的樹形菜單,是在權限系統裏面動態配置的,菜單的配置界面以下所示。post
上面的選擇圖圖標就是咱們須要動態設置的圖標,因爲圖標資源咱們是以圖片形式存儲在對應的記錄裏面的,所以使用起來也是比較方便的,咱們在配置的時候,獲取到對應的圖標資源並存儲起來便可。優化
除了上面能夠參考從DevExpress內置圖標資源獲取圖標的方式外網站
咱們還能夠選擇咱們本身喜歡的圖標資源,也就是從系統圖標文件中選擇本身喜歡的,以下界面所示。this
所以我考慮在運行時刻整合兩種不一樣選擇圖標的方式。
咱們先來看看我整合後的圖表選擇界面,以下所示,包含了運行時刻提取DevExpress內置圖標的功能和從系統文件中選擇圖標的功能。
三、運行時刻提取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(64, 64); //返回處理 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 != 1) { throw new Exception("讀取異常"); } return new System.Resources.ResourceReader(imagesAssembly.GetManifestResourceStream(imageResources[0])); }
另外,咱們根據圖表的文件名結構,咱們經過正則表達式來讀取它的對應信息,而後把它的大小、類別、集合信息存儲起來。
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(0, 30, 0));//30分鐘過時 return result; } }
以上代碼經過
public static DXImageGalleryLoader Default
定義了一個靜態的實例屬性,這樣這個 DXImageGalleryLoader 實例只會在程序第一次使用的時候構建並加載圖片資源,後續都是從緩存裏面讀取,提升響應速度的同時,也會記住上次的選擇界面內容。
以上就是整個功能的處理思路,以及一步步的優化處理,以便實現功能展現的同時,也提升響應速度,最終界面就是咱們開始的時候介紹的那樣。
單擊或者選中系統圖標後, 須要設置的按鈕或者界面,就會及時更新圖標展現,體驗效果仍是很是不錯的。
因爲這個界面功能的通用性,我把它做爲系統界面基礎模塊,放到了個人框架BaseUIDx裏面,各個系統模塊均可以調用了。