Question Help. Can't assign an array of images to an ImageList.

Mugsy

Member
Joined
Sep 30, 2020
Messages
16
Programming Experience
10+
I'm trying to sort images before assigning them to an ImageList object.

Using a "For... Each" loop, I can assign images one at a time as they are read directly from the computer.

The problem is, this method reads the files alphabetically, so it reads "image09.png", "image10.png", "image100.png", "image101.png", "image102.png", etc before "image11.png" (physically renumbering the users files is not an option.)

My "solution" was to read the images into an array of type Image, inserting images into the array in order, then assign that array to my "ImageList" object.

This results in an assignment error, and I have no idea why. :cry:

Can't assign image array to an ImageList (error on line 11):
Dim imgImageArray(200) As Image
...
For Each ClosetFile As String In IO.Directory.GetFiles(strClosetPath, strCategory & "*.png", SortOrder.Ascending)
...
       Dim img = Image.FromFile(ClosetFile)
       imgImageArray(intPosition) = img ' "intPosition" parsed from filename.
...
Next ' ending For/Each loop.

For intCnt = 1 To intTotalNumberOfFiles ' Load image array with sorted list.
    ImageList2.Images.Add(imgImageArray(intCnt))
    ' also tried: ".AddRange" w/o the loop.
Next

Is there any other way to assign images to an ImageList control other than directly from disk?

TIA
 
Last edited:
terminology is important in programming so I have some corrections.

ImageList is not a control. It is a component, which is all that's required for use in the designer. A control is a class that inherits Control, which Imagelist does not. That's why it's in the Components section of the Toolbox.

You don't assign anything to an object. You assign objects to variables or properties. you might assign an object to a list item or array element or you might add an object to a list.
 
With regards to sorting your file paths, there is a Windows API function that you can use to sort the same way that Windows does in File Explorer. The first step is to declare the unmanaged function:
VB.NET:
Imports System.Runtime.InteropServices

Public Module NativeMethods

    <DllImport("shlwapi.dll", CharSet:=CharSet.Unicode)>
    Public Function StrCmpLogicalW(x As String, y As String) As Integer
    End Function

End Module
You can then use that method in a number of ways. If it's a one-off sort of an array, the simplest option is to use the overload of Array.Sort that takes a Comparison(Of T) delegate as an argument:
VB.NET:
Dim filePaths = Directory.GetFiles(strClosetPath, strCategory & "*.png")

Array.Sort(filePaths, AddressOf NativeMethods.StrCmpLogicalW)
You can then check the contents of that array and you'll see that it is sorted alphanumerically, just as File Explorer does.
 
As for the issue, you can't set an item in the ImageList that doesn't already exist. If the list is initially empty, you need to Add images to it. You should ALWAYS prefer calling AddRange once to calling Add multiple times. Only do the latter if there will be a break between the Add calls. In this case, you can create all the Image objects first and then add them all in a single batch. There are a number of variations in ways to create the Images. You could use a loop:
VB.NET:
Dim upperBound = filePaths.GetUpperBound(0)
Dim images(upperBound) As Image

For i = 0 To upperBound
    images(i) = Image.FromFile(filePaths(i))
Next
The simplest option is to use LINQ though:
VB.NET:
Dim images = filePaths.Select(Function(s) Image.FromFile(s)).ToArray()
Either way, once you have an Image array, you simply pass it to AddRange:
VB.NET:
ImageList2.Images.AddRange(images)
I'm not sure why your original code didn't work. At a glance it seems that it should but, given that you would have been given an error message as a diagnostic aid, you should have given that to us, so that we know what we're looking for. Regardless, this code is tested and works. Just note that, because you are calling Image.FromFile, all the files will be locked. If you're going to be done with those Image objects before the application closes, be sure to call Dispose on each one, so as to release the files.
 
As for the issue, you can't set an item in the ImageList that doesn't already exist. If the list is initially empty, you need to Add images to it. You should ALWAYS prefer calling AddRange once to calling Add multiple times. Only do the latter if there will be a break between the Add calls. In this case, you can create all the Image objects first and then add them all in a single batch. There are a number of variations in ways to create the Images. You could use a loop:
VB.NET:
Dim upperBound = filePaths.GetUpperBound(0)
Dim images(upperBound) As Image

For i = 0 To upperBound
    images(i) = Image.FromFile(filePaths(i))
Next
The simplest option is to use LINQ though:
VB.NET:
Dim images = filePaths.Select(Function(s) Image.FromFile(s)).ToArray()
Either way, once you have an Image array, you simply pass it to AddRange:
VB.NET:
ImageList2.Images.AddRange(images)
I'm not sure why your original code didn't work. At a glance it seems that it should but, given that you would have been given an error message as a diagnostic aid, you should have given that to us, so that we know what we're looking for. Regardless, this code is tested and works. Just note that, because you are calling Image.FromFile, all the files will be locked. If you're going to be done with those Image objects before the application closes, be sure to call Dispose on each one, so as to release the files.

Thx. I'll give this a try this afternoon (busy day.)

The error (as noted in my question) was a simple "assignment error". No more detail given in the runtime error message.
 
There's no need to quote a whole post just to say "thanks, will do". There was no point to quoting that post at all. You're just making the thread harder to follow. You only need to quote something if there's a specific reason to single that part out to make it clear that that's what you're referring to. If I'm the only one who's replied and your responding to the post as a whole then there's nothing to single out so no point quoting.
 
The error (as noted in my question) was a simple "assignment error". No more detail given in the runtime error message.
There's no way that's true. There would have been an actual error message. They are provided for a reason.
 
(Sorry for the late reply. Death in the family.)

I always have difficulty implementing "<DllImport(..." statements. They're always redlined when I include the code (added to a module in this case).

I can't add/test the remaining code till I straighten that out.

TIA.
 
If they are underlined in red then there's a compilation error, which means there's an error message, which tells you what the problem is. Perhaps you could have posted that error message and we could have already solved the problem for you. The code I posted has been tried and tested so all you had to do was copy and paste it. My guess would be that you haven't imported the appropriate namespace but I can only guess at this point.
 
If they are underlined in red then there's a compilation error, which means there's an error message, which tells you what the problem is. Perhaps you could have posted that error message and we could have already solved the problem for you. The code I posted has been tried and tested so all you had to do was copy and paste it. My guess would be that you haven't imported the appropriate namespace but I can only guess at this point.

My apologies, but I have since made significant changes to the code, so that particular error is gone so I can't reproduce it.
At the time, that particular bit of code was not underlined in red (or else the program never would have run & given me a runtime error.)

After making changes, I'm still back to my original problem of not being able to assign anything to my "ImageList" other than loading the images directly into it from disk.
 
After making changes, I'm still back to my original problem of not being able to assign anything to my "ImageList" other than loading the images directly into it from disk.
I've already shown you how to solve your original problem so I guess we're done here. If you can't show me how you've used the code I provided and explain exactly how it doesn't do as you require then there's really nothing more to be done.
 
I've already shown you how to solve your original problem so I guess we're done here. If you can't show me how you've used the code I provided and explain exactly how it doesn't do as you require then there's really nothing more to be done.

You never answered my question on how to implement "<DllImport(..." (your code does not work), so I suppose we are done here.
 
Am I being punked here? My code works as is. I literally just tested it in brand new Console projects targeting both .NET Framework 4.8 and .NET 6. I copied the code directly from this thread and it worked exactly as expected. If your code is not working then you did it wrong. I already told you that, if there's an error, you need to post the error message. Not only did you not post an error message but you also said that you couldn't reproduce the error, yet now you're saying that there's an error and it's my fault. Do you want my help or not? I'm trying to provide it but you have to do your part. I can't read your mind. You need to provide a clear problem statement as well as all the relevant diagnostic information. Last chance.
 
Am I being punked here? My code works as is. I literally just tested it in brand new Console projects targeting both .NET Framework 4.8 and .NET 6. I copied the code directly from this thread and it worked exactly as expected. If your code is not working then you did it wrong. I already told you that, if there's an error, you need to post the error message. Not only did you not post an error message but you also said that you couldn't reproduce the error, yet now you're saying that there's an error and it's my fault. Do you want my help or not? I'm trying to provide it but you have to do your part. I can't read your mind. You need to provide a clear problem statement as well as all the relevant diagnostic information. Last chance.

Your code may work for you as a stand-alone program, but it does not integrate into what I have (using dotnet 4.8.1). I do not know what "conflicts" could be the source of the errors I encounter with your code.

The issue stems from having to rewrite my own code to use your method, and that's no simple task. One change can lead to a dozen other errors, making integration difficult. I'm trying to avoid rewriting my entire program just to use your code, and unfortunately, I can't show/share most of it.

Simply assigning data to an "ImageList" from a source other than direct storage shouldn't be this difficult. Hopefully, they will simplify this in a future version of .dotnet.
 
The issue stems from having to rewrite my own code to use your method, and that's no simple task.
Except it is a simple task. You add a module named NativeMethods to your project and you copy the code from post #3 into it. You then change this:
VB.NET:
For Each ClosetFile As String In IO.Directory.GetFiles(strClosetPath, strCategory & "*.png", SortOrder.Ascending)
to this:
VB.NET:
Dim filePaths = IO.Directory.GetFiles(strClosetPath, strCategory & "*.png")

Array.Sort(filePaths, AddressOf NativeMethods.StrCmpLogicalW)

For Each ClosetFile As String In filePaths
Done. What is the actual problem here?
You could even write your own method like this:
VB.NET:
Private Function GetFiles(path As String, searchPattern As String) As String()
    Dim filePaths = IO.Directory.GetFiles(path, searchPattern)

    Array.Sort(filePaths, AddressOf NativeMethods.StrCmpLogicalW)
    
    Return filePaths
End Function
and then change your code to this:
VB.NET:
For Each ClosetFile As String In GetFiles(strClosetPath, strCategory & "*.png")
Once you've written that method, the changes to your existing code are even less significant than before. Again, what is the actual problem?
 
Back
Top