Flowlayoutpanel child controls?

Zexor

Well-known member
Joined
Nov 28, 2008
Messages
520
Programming Experience
3-5
I have a flowlayoutpanel, and i am dynamically adding empty pictureboxes to it. Then do a bunch of things. Then how do i add image to a certain pictureboxes in the panel? can i refer to the picturebox by name?
 
can i refer to the picturebox by name?
yes: flp.Controls("the name")
 
the name as in when you create the picturebox you put a name in picturebox.name ? hmm i thought i tried that. when i go flp.Controls("name").image , it always say image is not a member of control.
 
Hi,

You need to cast the control to the correct type to access it's properties. i.e:-

VB.NET:
Dim myImage As Image
myImage = DirectCast(flp.Controls("Pic1"), PictureBox).Image
Cheers,

Ian.




 
the name as in when you create the picturebox you put a name in picturebox.name ? hmm i thought i tried that. when i go flp.Controls("name").image , it always say image is not a member of control.

The issue with that code is that Image is not a member of the Control class. Hopefully you have Option Strict On, in which case implicit conversions and late-binding are not permitted. All casts and conversions need to be explicit. The Controls collection of the form contains all the controls, including Buttons, TextBoxes and PictureBoxes. As such, the Item property, which you are using when you index the collection, returns everything as a Control reference. If you want to access members of a specific type of control then you must cast that reference as that type. Image is a member of PictureBox, not Control, so you must cast your Control reference as type PictureBox in order to access members of type PictureBox.
 
is it good practice to have Option Strict On? I seems to have a lot of implicit conversions and late binding.
 
It is very good practice to have Option Strict On. Most experienced developers lament the fact that Off is the default. That does make it easier for beginners to get simple things done but it also makes it much easier to break things as you get even a little bit more adventurous. I recommend turning it On for your current project and also in the IDE options so that it will be On by default for all future projects.

It won't stop you writing any bad code though. In this case, in order to access a member of a PictureBox you must cast the Control reference as type PictureBox. If the control itself is not a PictureBox though, you'll still get an InvalidCastException. Your intent will be more obvious though and the location of the issue too.
 
i really don't understand how to fix late binding

VB.NET:
Dim totalFiles As Integer = e.UserState(0)
VB.NET:
 sender.image =img


all those give me late binding errors

VB.NET:
            bgwLoadImage.ReportProgress(i, {file.GetUpperBound(1), file(1, i), img, file(3, i), file(4, i), file(5, i)})

and this say i cant infer an element type?
 
As I have said before, in order to access members of a certain type you have to have a reference of that type and if you don't have a reference of that type then you must cast. What is the type of 'e.UserState'? It's Object, right? Does Object have a default propery such that you can index it? No it doesn't. You have to cast 'e.UserState' as whatever type the object is that you assigned to it in the first place.

The same goes for 'sender'. It is type Object and Object has no Image property. Presumably that's handling an event of a PictureBox so you need to cast 'sender' as type PictureBox.

As for the last issue, if you want the compiler to infer the type of an array then all elements must have some common type other than Object, i.e. they must all be, inherit or implement some type other than Object. That is not the case for your code so you have to be explicit with the type of the array, i.e. you have to specify that it is type Object. That said, using an Object array like that is rather slack. You should either use a Tuple or else define your own type specifically for that purpose.
 
so first one is
VB.NET:
  Dim totalFiles As Integer = CInt(DirectCast(e.UserState, Object())(0))
2nd one
VB.NET:
DirectCast(sender, PictureBox).Image = img
last one
VB.NET:
bgwLoadImage.ReportProgress(i, New Object() {file(0, i), file(1, i), img, file(3, i), file(4, i), file(5, i), file.GetUpperBound(1)})
i also have another one like this give error when run
VB.NET:
tssl1.text = sender.tag(2).tostring
tssl1.text = CStr(DirectCast(sender, PictureBox).Tag)(2)
so much more complicated with all the casts, like this one is so much harder to read
VB.NET:
Dim img As Image = e.UserState(2)
Dim img As Image = DirectCast(DirectCast(e.UserState, Object())(2), Image)
 
Last edited:
Firstly, noone said that you had to perform multiple casts on one line. You can break it up onto multiple lines if you want the code to be clearer. Secondly, if you had declared a typed specifically to pass the data rather than using an Object array then you would have much less casting to do. If you don't want all the casting then don't take the lazy option of using an Object array. That 2D array suggests more of the same. If you do it the proper way and declare types where it's appropriate then your code will be even more type-safe, more robust and easier to read.
 
i am saving some data in the tag of the picture box, so when i click it, i can just get it from the tag. how do you fix this late binding?
VB.NET:
tssl1.Text = sender.Tag(2)
like this??
VB.NET:
tssl1.Text = DirectCast(sender, PictureBox).Tag(2).toString
this still give last binding hmm

VB.NET:
tssl1.Text = DirectCast(DirectCast(sender, PictureBox).Tag, Object())(2).ToString
hehe 3 times longer
 
Last edited:
Your point being? I reckon that it probably takes longer to build buildings to make them earthquake-proof. Maybe they shouldn't do that either. I mean, most of the time it won't matter so why bother?

If it's such a drain on you to do the casting then, as I have said more than once, define your own types. You can inherit the PictureBox and add a property of the appropriate type and then use that property instead of the Tag. That's one less cast. You can also define a class to store the data with a property of the appropriate type for each value. That's another cast not required. I'm sure you're right though. They should just make everything an object. Why have I been wasting my time all these years?
 
Hi,

Maybe I can try and put a fresh view on this for you and also give jmcilhinney a rest.

To try and help you understand what jmcilhinney is trying to tell you please see the code below. Read through the comments carefully and hopefully you should be able to grasp what is being said to you.

The first thing you have to realise is that you cannot treat the tag property as an array as it seems you are doing with .tag(2). What jmcilhinney is trying to tell you is that if you want to save multiple elements of data in the tag property then you have to do that by creating and using your own typed properties. This can be done by using Inheritance of the PictureBox class and expanding on it's existing properties or by creating a separate class to hold your data. In this example I will create a separate class to hold the additional data in the tag property.

Create a new form, add a timer with enabled = true and interval = 2000. Also add a FlowLayoutPanel and name it flp. In the timer event change the image to an image on your own machine.

VB.NET:
Public Class Form1
 
  Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
    'I have used this timer to simulate the pictures being add to your own flow layout panel
    Static Counter As Integer
 
    'firstly create a SINGLE instance of the class that is going to hold all the details
    'that you want to save in the tag property of the picture box
    'See the class myTagDetails defined below to hold the information for the tag
    Dim myTagInfo As New myTagDetails
    With myTagInfo
      .PicName = "Sample Pic"
      .PicSize = 1000
      .TimeAdded = Now()
    End With
 
    'Now create the pciturebox for the new image and set whatever properties you need
    'finally add the myTagInfo variable to the tag property. Remember this is a single 
    'instance of the class but it has multiple properties
    Dim PicBox As New PictureBox
    With PicBox
      .Name = "PictureBox" & Counter
      .Image = My.Resources.BionatureLoginPic 'add your own picture here
      .Width = 50
      .Height = 50
      .Tag = myTagInfo
    End With
 
    'add the picturebox to the flowlayoutpanel and add an event handler for the click event
    flp.Controls.Add(PicBox)
    AddHandler PicBox.Click, AddressOf PictureBoxClicked
  End Sub
 
  Private Sub PictureBoxClicked(sender As System.Object, e As System.EventArgs)
    'so here is the event handler that is processed when each button is clicked
    'Firstly you will see that DirectCast is used to convert the sender object to type PictureBox
    Dim ClickedPicBox As PictureBox = DirectCast(sender, PictureBox)
    'Once we have got a Valid PictureBox type we can then convert the tag property which is now an object to its correct type
    'bring myTagDetails using the same principal as above
    Dim PicTagDetails As myTagDetails = DirectCast(ClickedPicBox.Tag, myTagDetails)
    'now that we have two correctly typed variables we can now do whatever we want with the selected picturebox.
    MsgBox(PicTagDetails.PicName)
    MsgBox(PicTagDetails.PicSize)
    MsgBox(PicTagDetails.TimeAdded)
  End Sub
 
  Private Class myTagDetails
    'Here we define a class that has multiple properties that be changed and modified as needed
    'Any variable type can be used here to do whatever you want
    Public Property PicName As String
    Public Property PicSize As Integer
    Public Property TimeAdded As DateTime
  End Class
End Class
Hope that helps.

Cheers,

Ian
 
Ok i am going to do that from now on. The code is much easier to read, thanks.
But Why cant you treat .tag as an array? It's just an object? Can't you just put an object array in it?
So i should make another class for e.UserState if i want to send an object array too?
 
Last edited:
Back
Top