Learn how to use a class project to get images from parent Windows forms project.
Note
Even in 2023 Windows Forms are very useful, close behind Console projects for various usages from developer tools to internal business solutions.
First attempt
Write all required code in a form. This is the typical approach for new developers and of course tied to one form in a project so the code is not reusable.
Second attempt
Break out code outside of a single form with a class to retrieve all images then have methods to get only icons and a method for Bitmap image.
Although code has been abstracted from a single form, the code is still tied to a specific namespace.
For an example, see the following project.
Solution
Note
The following has been pre-done in this article's source code.
Class project
Break code to retrieve images from a project to a class project.
Create a new class project, double click on the project name in Solution Explorer and replace content with the following and save.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>disable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
Add the following class for remembering images in a calling project resources.
Notes
- The property IsIcon which indicates the image is an Icon, not a Bitmap.
- Override ToString is for the demo which becomes the DisplayMemer for a ListBox.
public class ResourceItem
{
/// <summary>
/// Resource name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Image which is either an icon or bitmap
/// </summary>
public Bitmap Image { get; set; }
public Icon Icon { 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;
}
Add the following class to read resources were the calling code passes the ResourceManager to the class project.
/// <summary>
/// Read images from current project resources
/// </summary>
public class ImageHelper
{
/// <summary>
/// Get all bitmap and icon resources
/// </summary>
/// <returns></returns>
public static List<ResourceItem> ResourceItemList(ResourceManager sender)
{
var items = new List<ResourceItem>();
foreach (var name in sender.ResourceImageNames())
{
ResourceItem item = new() { Name = name, IsIcon = false };
if (sender.GetObject(name) is Icon icon)
{
item.Image = ((Icon)sender.GetObject(name))?.ToBitmap();
item.Icon = icon;
item.IsIcon = true;
}
else
{
item.Image = (Bitmap)sender.GetObject(name);
}
items.Add(item);
}
return items;
}
}
Next we add the following class to get the parent project resource names.
public static class ResourceExtensions
{
public static List<string> ResourceImageNames(this ResourceManager sender)
{
try
{
var names = new List<string>();
var resourceSet = sender
.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
names.AddRange(
from DictionaryEntry dictionaryEntry in resourceSet
where dictionaryEntry.Value is Image || dictionaryEntry.Value is Icon
select dictionaryEntry.Key.ToString());
return names;
}
catch (Exception)
{
return null;
}
}
}
Parent form
Note
If there are no images the project presented will not build as the Properties class will not exists.
Objectives
- To dynamically change the form's icon when traversing a ListBox.
- Detect if there are icon and Bitmap images
- Find a image (Icon and Bitmap) by name for the purpose, in this case display to a PictureBox.
To start, add a reference to the above class project to this project.
💡 An easy method, in Solution Explorer, drag the class project node to the form project node and release the mouse. Visual Studio adds the reference to the form project.
Next a Singleton (thread safe) class is added. Responsible for loading images by calling ImageHelper.ResourceItemList from the class project above.
public sealed class ResourceImages
{
private static readonly Lazy<ResourceImages> Lazy = new(() => new ResourceImages());
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(Resources.ResourceManager);
/// <summary>
/// Icons from resources
/// </summary>
public List<ResourceItem> Icons => Images().Where(resourceItem
=> resourceItem.IsIcon).OrderBy(resourceItem => resourceItem.Name).ToList();
/// <summary>
/// BitMaps from resources
/// </summary>
public List<ResourceItem> BitMaps => Images().Where(resourceItem
=> resourceItem.IsIcon == false).OrderBy(resourceItem => resourceItem.Name).ToList();
/// <summary>
/// Are there any icons in resources
/// </summary>
public bool HasIcons => Icons.Any();
/// <summary>
/// Are there any icons in resources
/// </summary>
public bool HasBitmaps => BitMaps.Any();
}
Form code
- Place two ListBoxes on the form to display image names
- Place two PictureBoxes on the form to display images
Form load event
Check if there are icons, if there are assign a List of ResourceItem to the ListBox which contains the image. Subscribe to SelectedIndexChanged event.
Next search for a known Icon by name, set the SelectedIndex which fires off the event for selected index changed which in turn calls a method to display the image in a PictureBox.
public Form1()
{
InitializeComponent();
if (ResourceImages.Instance.HasIcons)
{
IconListBox.DataSource = ResourceImages.Instance.Icons;
IconListBox.SelectedIndexChanged += IconListBoxOnSelectedIndexChanged;
IconListBox.SelectedIndex = IconListBox.FindString("Csharp");
}
if (ResourceImages.Instance.HasBitmaps)
{
BitmapListBox.DataSource = ResourceImages.Instance.BitMaps;
BitmapListBox.SelectedIndexChanged += BitmapListBoxOnSelectedIndexChanged;
BitmapListBox.SelectedIndex = BitmapListBox.FindString("earth-11602");
}
var images = ResourceImages.Instance.Images();
imageCountLabel.Text = $"There are {images.Count} in this project's resources";
}
Methods to display Icon and Bitmap and adjust the PictureBox size dependent on the image type.
private void ShowCurrentIcon()
{
if (IconListBox.SelectedIndex <= -1) return;
var item = (ResourceItem)IconListBox.SelectedItem;
pictureBox1.SizeMode = item!.IsIcon ? PictureBoxSizeMode.Normal : PictureBoxSizeMode.Zoom;
pictureBox1.Image = item.Image;
// change this form's icon
Icon = item.Icon;
}
private void ShowCurrentBitmap()
{
if (BitmapListBox.SelectedIndex <= -1) return;
var item = (ResourceItem)BitmapListBox.SelectedItem;
pictureBox2.SizeMode = item!.IsIcon ? PictureBoxSizeMode.Normal : PictureBoxSizeMode.Zoom;
pictureBox2.Image = item.Image;
}
Summary
There are two lessons presented. First, how to refactor code and secondly, how to get to images in a project's resource which can e expanded on to get other resource types.
Source code
Clone the following GitHub repository which code is written with .NET Core 8.