Get images from resources in Windows Forms
Tags: csharp, coding, tutorial, frontend
Published: 2023-09-08 14:09:13

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.


![Example form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejkq6sr7e6e20emeiinf.png)

## 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](https://github.com/karenpayneoregon/windows-forms-samples/tree/master/WinFormsExamples). 

## Solution

> **Note**
> The following has been pre-done in this article's [source code](https://github.com/karenpayneoregon/winform-core-resources).

### 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.

```xml
<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.

```csharp
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 ](https://learn.microsoft.com/en-us/dotnet/api/system.resources.resourcemanager?view=net-7.0)to the class project.

```csharp
/// <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.

```csharp
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.

:bulb: 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.

```csharp
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](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listbox.selectedindexchanged?view=windowsdesktop-7.0) event.

Next search for a known Icon by name, set the [SelectedIndex](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listbox.selectedindex?view=windowsdesktop-7.0) which fires off the event for selected index changed which in turn calls a method to display the image in a PictureBox.

```csharp
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.

```csharp
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](https://github.com/karenpayneoregon/winform-core-resources) which code is written with .NET Core 7.