With modern technologies such as MAUI for desktop solutions there is still a place for Windows Form projects from new developers just starting out to quickly building internal utility projects.
In this article learn how to show images from a project’s resources in a windows form working with .NET Core 7 Window Forms and a .NET Core 7 class project (optional).
Source code
Clone the following GitHub repository.
Note
I've seen this question pop-up on various forms over the past 20 years and this is one reason for this article.
Let's begin
Although the code could be completely written in a single form which means to use said code in another project the developer must pick through the code, copy and paste into another project. There is a project done this way while another project uses a class project for base code to read images from the calling project resources.
Feel free to examine code for the project ChangeImage
were all need code is in the project.
We will focus on using a class project, ResourceLibrary
.
The model for storing resource information.
public class ResourceItem
{
/// <summary>
/// Resource name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Image which is either an <see cref="Icon"/> or <see cref="Bitmap"/>
/// </summary>
public Bitmap Image { get; set; }
/// <summary>
/// Indicates if dealing with an icon so when displaying the
/// control used to display can adjust it's size or Size mode
/// </summary>
public bool IsIcon { get; set; }
public override string ToString() => Name;
}
The following class reads images of type Icon
and Bitmap
using System.Collections;
using System.Drawing;
using System.Globalization;
using System.Resources;
using ResourceLibrary.Models;
namespace ResourceLibrary;
public class ImageHelper
{
public static List<string> ResourceImageNames(ResourceManager manager)
{
var names = new List<string>();
var resourceSet = manager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
names.AddRange(resourceSet!.Cast<DictionaryEntry>()
.Where(dictionaryEntry => dictionaryEntry.Value is Image ||
dictionaryEntry.Value is Icon)
.Select(dictionaryEntry => dictionaryEntry.Key.ToString()));
return names;
}
/// <summary>
/// Get all bitmap and icon resources
/// </summary>
/// <returns></returns>
public static List<ResourceItem> ResourceItemList(ResourceManager manager)
{
var items = new List<ResourceItem>();
foreach (var name in ResourceImageNames(manager))
{
var item = new ResourceItem()
{
Name = name,
IsIcon = false
};
if (manager!.GetObject(name) is Icon)
{
item.Image = ((Icon)manager.GetObject(name)!)?.ToBitmap();
item.IsIcon = true;
}
else
{
item.Image = (Bitmap)manager.GetObject(name)!;
}
items.Add(item);
}
return items;
}
}
Next, several language extensions which filter all resource list to a specific type.
public static class ResourceItemExtensions
{
/// <summary>
/// Return resources of type Icon
/// </summary>
/// <param name="sender"></param>
/// <returns>list of icons or an empty list</returns>
public static List<ResourceItem> Icons(this List<ResourceItem> sender)
=> sender.Where(item => item.IsIcon).ToList();
/// <summary>
/// Return resources of type Bitmap
/// </summary>
/// <param name="sender"></param>
/// <returns>list of bitmaps or an empty list</returns>
public static List<ResourceItem> BitMaps(this List<ResourceItem> sender)
=> sender.Where(item => !item.IsIcon).ToList();
}
In a forms project, add a ListBox and PictureBox with the PictureBox large enough to all all images to be displayed. Later in code there is a method to adjust the PictureBox SizeMode.
Add a reference to the class project.
Add the following class which can be used in other projects.
public sealed class ResourceImages
{
private static readonly Lazy<ResourceImages> Lazy
= new(() => new());
public static ResourceImages Instance => Lazy.Value;
private List<ResourceItem>? _images;
/// <summary>
/// Get all icon and bitmap images from project resources
/// </summary>
/// <returns>list of images</returns>
public List<ResourceItem> Images()
{
_images = ImageHelper.ResourceItemList(
Properties.Resources.ResourceManager);
return _images;
}
}
To display all images, add a BindingSource to the form.
private readonly BindingSource _allBindingSource = new ();
In Form OnShown event, add the following code to get all images.
_allBindingSource.DataSource = ResourceImages.Instance
.Images()
.OrderBy(x => x.Name)
.ToList();
Add the following method to the form for displaying the current image when the user changes the selected item in the ListBox.
private void ChangeFromAllImage()
{
if (AllImagesListBox.SelectedIndex <= -1) return;
var item = (ResourceItem) _allBindingSource.Current;
AllImagesPictureBox.SizeMode = item!.IsIcon ? PictureBoxSizeMode.Normal : PictureBoxSizeMode.Zoom;
AllImagesPictureBox.Image = item.Image;
}
And,
private void BitmapBindingSource_PositionChanged(object? sender, EventArgs e)
{
ChangeFromBitmapImage();
}
Back to OnShown event, add the following lines which
Displays the image names in the ListBox as
ResourceItem
overrides ToString which the ListBox uses for DisplayMember.Subscribes to OnPosition changed which called ChangeFromAlImages which in turn displays the appropriate image.
AllImagesListBox.DataSource = _allBindingSource;
_allBindingSource.PositionChanged += AllBindingSource_PositionChanged;
ChangeFromAllImage();
Caveat
Suppose there is a need to display a specific image? Although a BindingSource has a Find method, it is not supported for a list. The work around is to use a BindingList which is populated with the images via
_bindingList = new BindingList<ResourceItem>(ResourceImages.Instance
.Images()
.BitMaps()
.OrderBy(x => x.Name)
.ToList());
Setup as before but with the BindingList
_bitmapBindingSource.DataSource = _bindingList;
BitmapImagesListBox.DataSource = _bitmapBindingSource;
Now to find a specific image.
var resourceItem =_bindingList.FirstOrDefault(x => x.Name == "Miata2");
if (resourceItem != null) _bitmapBindingSource.Position =
_bitmapBindingSource.IndexOf(resourceItem);