在Winform開發(fā)框架中使用DevExpress的內(nèi)置圖標資源

在開發(fā)Winform程序界面的時候送巡,我們往往會使用一些較好看的圖表柴信,以便能夠為我們的程序界面增色,良好的圖標設(shè)置可以讓界面看起來更加美觀舒服蚯舱,而且也比較容易理解,圖標我們可以通過一些網(wǎng)站獲取各種場景的圖標資源掩蛤,不過本篇隨筆主要介紹如何利用DevExpress的內(nèi)置圖標資源來實現(xiàn)界面圖標的設(shè)置枉昏。

1、設(shè)計時刻的圖標處理

豐富的圖標處理盏档,在菜單凶掰、工具欄、樹列表等地方蜈亩,以及按鈕等地方懦窘,都可以使用,而這些我們可以利用DevExpress的內(nèi)置圖標選擇來減輕我們尋找合適圖標的煩惱稚配。

[圖片上傳失敗...(image-25bc05-1551341754305)]

一些按鈕畅涂、工具欄等的圖標設(shè)置,一般是固定的道川,我們往往可以在設(shè)計時刻就指定它午衰,這樣我們可以使用本地的圖標,也可以使用DevExpress的內(nèi)置圖標冒萄。而使用DevExpress內(nèi)置圖標資源的時候臊岸,我們可以調(diào)出DevExpress的內(nèi)置圖標選擇框的。

如下是按鈕添加圖標方式尊流,操作非常簡單帅戒,在按鈕的右上角小圖標上單擊一下進入編輯界面,如下所示崖技。

[圖片上傳失敗...(image-952964-1551341754305)]

然后選擇Image按鈕逻住,進入圖標選擇界面,選擇內(nèi)置的DevExpress圖標庫即可迎献,基本上瞎访,只要是DevExpress的原生控件,那么就可以通過這種內(nèi)置圖標的對話框進行圖標選擇吁恍,非常方便扒秸。

image

2播演、運行時刻的圖標處理

上面的操作是在設(shè)計時候,DevExpress設(shè)計器給我們提供很多便利選擇內(nèi)置圖標伴奥,而在界面運行時刻宾巍,想動態(tài)處理界面按鈕圖標,或者樹形菜單的圖標的時候渔伯,就沒有這個直接的接口來設(shè)置圖標了顶霞,而我們框架的菜單往往都是需用動態(tài)增加的,因此圖標的設(shè)置也是在運行時刻的锣吼。如下面的樹列表中选浑,圖標就是動態(tài)指定的。

[圖片上傳失敗...(image-ee515e-1551341754305)]

這些動態(tài)的樹形菜單玄叠,是在權(quán)限系統(tǒng)里面動態(tài)配置的古徒,菜單的配置界面如下所示。

image

上面的選擇圖圖標就是我們需要動態(tài)設(shè)置的圖標读恃,由于圖標資源我們是以圖片形式存儲在對應(yīng)的記錄里面的隧膘,因此使用起來也是比較方便的,我們在配置的時候寺惫,獲取到對應(yīng)的圖標資源并存儲起來即可疹吃。

除了上面可以參考從DevExpress內(nèi)置圖標資源獲取圖標的方式外

image

我們還可以選擇我們自己喜歡的圖標資源,也就是從系統(tǒng)圖標文件中選擇自己喜歡的西雀,如下界面所示萨驶。

image

因此我考慮在運行時刻整合兩種不同選擇圖標的方式。

我們先來看看我整合后的圖表選擇界面艇肴,如下所示腔呜,包含了運行時刻提取DevExpress內(nèi)置圖標的功能和從系統(tǒng)文件中選擇圖標的功能。

image

3再悼、運行時刻提取DevExpress內(nèi)置圖標的功能實現(xiàn)

首先我們參考設(shè)計時刻的界面展示

image

來設(shè)計一個界面來展示圖標信息

image

參考原版的界面核畴,設(shè)計盡可能貼近即可,另外我們自己加入一個從系統(tǒng)選擇圖標資源的操作冲九。

image

至于圖標選中后我們返回對應(yīng)的Image對象給調(diào)用者谤草,則通過事件進行處理,以便選中后娘侍,即使更新顯示效果咖刃。

如下所示泳炉,我們定義一個委托和事件憾筏。

    /// <summary>
    /// DevExpress圖標和系統(tǒng)圖標選擇窗體
    /// </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>
        /// 處理圖標選擇的事件觸發(fā)
        /// </summary>
        public virtual void ProcessIconSelected(Image image, string name)
        {
            if (OnIconSelected != null)
            {
                OnIconSelected(image, name);
            }
        }

然后在內(nèi)置圖標顯示中,如果觸發(fā)圖標的單擊花鹅,我們就觸發(fā)事件氧腰,以便讓調(diào)用者更新界面顯示,如下代碼所示。

foreach (GalleryItem item in items[key])
{
    item.ItemClick += (s, e) =>
    {
        //選擇處理
        ProcessIconSelected(item.ImageOptions.Image, item.Description);
    };
}

而對于從系統(tǒng)文件加載文件進行顯示圖標的古拴,類似的觸發(fā)方式箩帚。

        /// <summary>
        /// 從系統(tǒng)資源中加載圖標文件,然后觸發(fā)事件進行顯示
        /// </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);
            }
        }

這樣我們在菜單的選擇圖標的時候黄痪,就可以觸發(fā)事件進行獲取圖表并更新自身了紧帕。

image
        private void btnSelectIcon_Click(object sender, EventArgs e)
        {
            FrmImageGallery dlg = new FrmImageGallery();
            dlg.OnIconSelected += (image, name) =>
            {
                this.txtEmbedIcon.Image = image;
            };
            dlg.ShowDialog();
        }

完成了這些處理痒谴,我們再次將焦點放在如何提取并展示DevExpress內(nèi)置圖標的身上珠漂。

[圖片上傳失敗...(image-6e9bc1-1551341820701)]

為了獲取圖表資源里面的分類及大小等信息,我們需要把圖標資源進行一個加載出來步责,然后讀取里面的類別和大小挺尾、集合等信息鹅搪。先定義幾個變量來承載這些信息。


        /// <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)

這個代碼它就是從資源里面進行獲取對應(yīng)的圖表資源涂召。

        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]));
        }

另外幔睬,我們根據(jù)圖表的文件名結(jié)構(gòu),我們通過正則表達式來讀取它的對應(yīng)信息芹扭,然后把它的大小麻顶、類別、集合信息存儲起來舱卡。

    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);
        }
    }      

有了這些資源,我們對它們進行搜索就顯得很方便了舍杜,我們?nèi)绻枰鶕?jù)文件名或者其他條件進行查詢集合的數(shù)據(jù)新娜,提供一個通用的方法即可,如下代碼所示既绩。

        /// <summary>
        /// 根據(jù)條件獲取集合
        /// </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");

                //如果是查詢處理饲握,把記錄放到查詢結(jié)果里面
                if (!string.IsNullOrEmpty(fileName))
                {
                    if(key.Contains(fileName))
                    {
                        list.Add(ImageCollection[key]);
                    }
                    dict["查詢結(jié)果"] = 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 進行搜索的了蚕键,不用再次讀取程序集并依次分析它,速度提供不少的衰粹。

由于圖表資源的處理是比較耗時的锣光,我們把整個圖標加載的類作為一個靜態(tài)的對象緩存起來,這樣下次使用直接從緩存里面拿铝耻,對應(yīng)的資源也不用重新加載誊爹,更好的提高我們重用的效果了,體驗更好了瓢捉。

    /// <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

定義了一個靜態(tài)的實例屬性,這樣這個 DXImageGalleryLoader 實例只會在程序第一次使用的時候構(gòu)建并加載圖片資源泊柬,后續(xù)都是從緩存里面讀取椎镣,提高響應(yīng)速度的同時,也會記住上次的選擇界面內(nèi)容兽赁。

以上就是整個功能的處理思路状答,以及一步步的優(yōu)化處理,以便實現(xiàn)功能展示的同時刀崖,也提高響應(yīng)速度惊科,最終界面就是我們開始的時候介紹的那樣。

image
image

單擊或者選中系統(tǒng)圖標后亮钦, 需要設(shè)置的按鈕或者界面馆截,就會及時更新圖標展示,體驗效果還是非常不錯的蜂莉。

由于這個界面功能的通用性蜡娶,我把它作為系統(tǒng)界面基礎(chǔ)模塊,放到了我的框架BaseUIDx里面映穗,各個系統(tǒng)模塊都可以調(diào)用了窖张。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蚁滋,隨后出現(xiàn)的幾起案子宿接,更是在濱河造成了極大的恐慌,老刑警劉巖辕录,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睦霎,死亡現(xiàn)場離奇詭異,居然都是意外死亡走诞,警方通過查閱死者的電腦和手機副女,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來速梗,“玉大人肮塞,你說我怎么就攤上這事∫鏊” “怎么了枕赵?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長位隶。 經(jīng)常有香客問我拷窜,道長,這世上最難降的妖魔是什么涧黄? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任篮昧,我火速辦了婚禮,結(jié)果婚禮上笋妥,老公的妹妹穿的比我還像新娘懊昨。我一直安慰自己,他們只是感情好春宣,可當(dāng)我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布酵颁。 她就那樣靜靜地躺著,像睡著了一般月帝。 火紅的嫁衣襯著肌膚如雪躏惋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天嚷辅,我揣著相機與錄音簿姨,去河邊找鬼。 笑死簸搞,一個胖子當(dāng)著我的面吹牛扁位,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播趁俊,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼贤牛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了则酝?” 一聲冷哼從身側(cè)響起殉簸,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沽讹,沒想到半個月后般卑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡爽雄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年蝠检,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挚瘟。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡叹谁,死狀恐怖饲梭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焰檩,我是刑警寧澤憔涉,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站析苫,受9級特大地震影響兜叨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衩侥,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一国旷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茫死,春花似錦跪但、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骨杂,卻和暖如春涂身,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搓蚪。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工蛤售, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妒潭。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓悴能,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雳灾。 傳聞我的和親對象是個殘疾皇子漠酿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容