WinForm application interface development practice – how to use DevExpress built-in icon resources?

When developing the Winform program interface, we often use some better-looking charts to add color to the program interface. Good icon settings can make the interface look more beautiful and comfortable, and it is also easier to understand. Icons We can obtain resources for various scenarios through some websites, but this article mainly introduces how to use DevExpress’s built-in icon resources to set interface icons.

PS: I would like to recommend an interface component that can be used in C# development – DevExpress WinForms. It can perfectly build smooth, beautiful and easy-to-use applications, whether it is an Office-style interface or analyzing and processing large quantities of business data. All can be done easily!

Click to get the official version of DevExpress v23.1(Q technical communication: 909157416)

1. Icon processing during design

Rich icon processing can be used in menus, toolbars, tree lists, etc., as well as buttons, etc. For these, we can use DevExpress’s built-in icon selection to alleviate our trouble of finding suitable icons.

How to use DevExpress built-in icon resources?

The icon settings of some buttons, toolbars, etc. are generally fixed, and can often be specified at the design time, so that we can use local icons or the built-in icons of DevExpresss. When using DevExpress’s built-in icon resources, you can call up DevExpress’s built-in icon selection box.

The following is how to add an icon to a button. The operation is very simple. Click the small icon in the upper right corner of the button to enter the editing interface, as shown below.

How to use DevExpress built-in icon resources?

Then select the Image button, enter the icon selection interface, and select the built-in DevExpress icon library. Basically, as long as it is a native control of DevExpress, you can select the icon through this built-in icon dialog box, which is very convenient.

How to use DevExpress built-in icon resources?

2. Icon processing at runtime

The above operation is during design. The DevExpress designer provides us with a lot of convenience to select built-in icons. However, when the interface is running, when we want to dynamically process interface button icons or tree menu icons, there is no direct interface to set them. Icons, and the menus of our framework often need to be added dynamically, so the settings of the icons are also at runtime. As in the tree list below, the icons are dynamically assigned.

How to use DevExpress built-in icon resources?

These dynamic tree menus are dynamically configured in the permission system. The menu configuration interface is as follows.

How to use DevExpress built-in icon resources?

The selection picture icon above is the icon we need to set dynamically. Since the icon resource is stored in the corresponding record in the form of a picture, it is relatively convenient to use. When configuring, we can obtain the corresponding icon resources and store them.

In addition to the above, you can refer to the method of obtaining icons from DevExpress built-in icon resources.

How to use DevExpress built-in icon resources?

We can also choose the icon resource we like, that is, choose the one we like from the system icon file, as shown in the following interface.

How to use DevExpress built-in icon resources?

So consider integrating two different ways of selecting icons at runtime.

Let’s first take a look at the integrated chart selection interface, as shown below, which includes the function of extracting DevExpress built-in icons at runtime and selecting icons from system files.

How to use DevExpress built-in icon resources?

3. Implementation of the function of extracting DevExpress built-in icons at runtime

First, we refer to the interface display during design:

How to use DevExpress built-in icon resources?

Let’s design an interface to display icon information:

How to use DevExpress built-in icon resources?

Referring to the original interface, the design should be as close as possible. In addition, we will add an operation to select icon resources from the system.

How to use DevExpress built-in icon resources?

As for when the icon is selected, we return the corresponding Image object to the caller, which is processed through events so that the display effect is updated even after the icon is selected.

As shown below, we define a delegate and event.

/// <summary>
/// DevExpress icon and system icon selection form
/// </summary>
public partial class FrmImageGallery : BaseForm
{
/// <summary>
/// Customize a delegate to handle icon selection
/// </summary>
public delegate void IconSelectHandlerDelegate(Image image, string name);

/// <summary>
/// Icon selection event
/// </summary>
public event IconSelectHandlerDelegate OnIconSelected;

private DXImageGalleryLoader loader = null;

public FrmImageGallery()
{
InitializeComponent();

InitDictItem();//Initialization
}

/// <summary>
/// Handle event triggering of icon selection
/// </summary>
public virtual void ProcessIconSelected(Image image, string name)
{
if (OnIconSelected != null)
{
OnIconSelected(image, name);
}
}

Then in the built-in icon display, if the click of the icon is triggered, we trigger the event so that the caller can update the interface display, as shown in the following code.

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

For loading files from system files to display icons, a similar trigger method is used.

/// <summary>
/// Load the icon file from the system resources, and then trigger the event for display
/// </summary>
private void txtFilePath_Properties_ButtonClick(object sender, ButtonPressedEventArgs e)
{
string file = GetIconPath();
if (!string.IsNullOrEmpty(file))
{
this.txtFilePath.Text = file; //Record file name
this.txtEmbedIcon.Image = LoadIcon(file);//Display image
this.txtEmbedIcon.Size = new System.Drawing.Size(64, 64);

//Return to processing
ProcessIconSelected(this.txtEmbedIcon.Image, file);
}
}

In this way, when we select an icon in the menu, we can trigger events to obtain the chart and update itself.

How to use DevExpress built-in icon resources?

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

After completing these processes, we once again focus on how to extract and display DevExpress built-in icons.

How to use DevExpress built-in icon resources?

In order to obtain the classification, size and other information in the chart resource, we need to load the icon resource one by one, and then read the category, size, collection and other information inside. First define a few variables to carry this information.

/// <summary>
/// Icon classification
/// </summary>
public List<string> Categories { get; set; }
/// <summary>
/// Icon collection
/// </summary>
public List<string> Collection { get; set; }
/// <summary>
/// Icon size
/// </summary>
public List<string> Size { get; set; }

The icon resource of DevExpress is in the assembly DevExpress.Utils.DxImageAssemblyUtil.ImageAssembly, so we need to read it and process each resource in turn.

Let’s take a look at the specific processing code, as shown below.

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

The operation code for reading resources is:

GetResourceReader(DevExpress.Utils.DxImageAssemblyUtil.ImageAssembly)

This code is to obtain the corresponding chart resources from the resources.

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("Read exception");
}
return new System.Resources.ResourceReader(imagesAssembly.GetManifestResourceStream(imageResources[0]));
}

In addition, according to the file name structure of the chart, we read its corresponding information through regular expressions, and then store its size, category, and collection information.

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

After the chart information is read, we need to parse it and store it, and put the Image object of the icon in a dictionary category to facilitate display according to groups.

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

With these resources, it is very convenient for us to search for them. If we need to query the collected data based on the file name or other conditions, we can provide a general method, as shown in the following code.

/// 
/// Get the collection based on conditions
/// 
/// 
public Dictionary Search(List collection, List categories,
List size, string fileName = "")
{
Dictionary dict = new Dictionary();

GalleryItemCollection list = new GalleryItemCollection();
foreach (var key in ImageCollection.Keys)
{
//Use regular expressions to obtain the collection, category, size and other information in the icon file name
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 it is query processing, put the record into the query result
if (!string.IsNullOrEmpty(fileName))
{
if(key.Contains(fileName))
{
list.Add(ImageCollection[key]);
}
dict["query results"] = list;
}
else
{
//If they are included in sets and lists, add them to the dictionary by category
if (collection.Contains(collectionItem) & amp; & amp;
categories.Contains(categoryItem) & amp; & amp;
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;
}

This search is directly based on the existing collection ImageCollection. There is no need to read the assembly again and analyze it in sequence, which provides a lot of speed.

Since the processing of chart resources is time-consuming, we cache the entire icon loading class as a static object, so that the next time it is used, it will be taken directly from the cache, and the corresponding resources do not need to be reloaded, which better improves reuse. It works and the experience is better.

/// 
/// Icon library loading processing
/// 
public class DXImageGalleryLoader
{
/// 
/// Icon dictionary category collection
/// 
public Dictionary ImageCollection { get; set; }
/// <summary>
/// Icon classification
/// </summary>
public List<string> Categories { get; set; }
/// <summary>
/// Icon collection
/// </summary>
public List<string> Collection { get; set; }
/// <summary>
/// Icon size
/// </summary>
public List<string> Size { get; set; }

/// 
/// Use cache processing to obtain object instances
/// 
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(keyName,
delegate () { return new DXImageGalleryLoader().LoadData(); },
new TimeSpan(0, 30, 0));//Expires in 30 minutes
return result;
}
}

The above code passes:

public static DXImageGalleryLoader Default

A static instance attribute is defined, so that this DXImageGalleryLoader instance will only build and load image resources when the program is used for the first time. Subsequent reads will be from the cache, which improves response speed and will also remember the last time. Select interface content.

The above is the processing idea of the entire function, as well as the step-by-step optimization process, so as to realize the function display and improve the response speed. The final interface is as we introduced it at the beginning.

How to use DevExpress built-in icon resources?

How to use DevExpress built-in icon resources?

After clicking or selecting the system icon, the icon display will be updated in time for the buttons or interfaces that need to be set, and the experience effect is still very good.

Due to the versatility of this interface function, I used it as the basic module of the system interface and placed it in my framework BaseUIDx, so that each system module can be called.

This article is reproduced from: Blog Park – Wu Huacong