How to modify label.text in a dynamically generated label in VB.net

V

vbnewbie

I am having problems accessing properties of dynamically generated
objects in VB2005. Can someone please help?
In a nutshell:
My app creates an equal number of checkboxes and labels that share the
same Tag number. (I thought it might help)
The checkboxes name is a concatenation of "chkCancel" and a number that

represents the order in which they were created:
chkCancel0 (Tag = 0)
chkCancel1 (Tag = 1)
chkCancel2 (Tag = 2)
etc...

The same goes for the labels


lblLabel0 (Tag = 0)
lblLabel1 (Tag = 1)
lblLabel2 (Tag = 2)
etc...


When the label is created, its text property is given a value
(lblLabel.text = value).


What I want to do, is clear that text property when I click on the
corresponding checkbox.
I want to do it with a common sub, handler, whatever, that will work
for all checkboxes.
My problem is how to acces the label text property of a dynamically
created label.


Pseudo code:


lableTag = sender.tag
if sender.checked=true then
lblLabel(labelTag).text="" (this is the part I have trouble with)

I don't seem to be able to reference any object created dynamically...


It must be very simple for an experienced programmer, but not to
vbnewbie... and it is driving me nuts!
Thanks
 
S

Stephany Young

Your event handler will go something like this:

Private Sub DynamicCheckBox_CheckChanged(ByVal sender As Object, ByVal e
As EventArgs)

Dim _tag As Object = CType(sender, Control).Tag

If _tag IsNot Nothing Then
Dim _c As Control = FindControl(Me, GetType(Label), _tag)
If _c IsNot Nothing Then CType(_c, Label).Text = String.Empty
End If

End Sub

Private Function FindControl(ByVal start As Control, ByVal type as Type,
tag As Object) As Control

For Each _c as Control In start.Controls
If _c.GetType() Is type Then
If _c.Tag IsNot Nothing AndAlso _c.Tag = tag Then Return _c
Else
If _c.HasChildren Then
Dim _cc As Control = FindControl(_c, type, tag)
If _cc IsNot Nothing Then Return _cc
End If
End If
Next

Return Nothing

End Function

When you 'dynamically generate' your CheckBox controls, subscribe to the
event handler with something like:

AddHandler chkCancel0.CheckChanged, AddressOf
DynamicCheckBox_CheckChanged
AddHandler chkCancel1.CheckChanged, AddressOf
DynamicCheckBox_CheckChanged

etc. Note that the same handler is used.

Note also that the event handler does not have a Handles clause.

When any one of you dynamic CheckBoxes is clicked, the event handler is
called and sender is a reference to the CheckBox that was clicked. We then
attempt to get a reference to a Label control anywhere on the form that has
a matching Tag property. If we get one then we set it's Text Property to an
empty string.

Let us konw how you get on.
 
M

mediocrevbprogrammer.freed

My code is shorter than Stephany Young's:

Private Sub ChkCancel_CheckChanged(ByVal sender As Object, ByVal e As
EventArgs) Handles chkCancel0.CheckChanged And chkCancel1.CheckChanged
And chkCancel2.CheckChanged And chkCancel3.CheckChanged
Select Case CType(sender, Control).Tag
Case 0
lblLabel0.Text=""
Case 1
lblLabel1.Text=""
Case 2
lblLabel2.Text=""
Case 3
lblLabel3.Text=""
End Select
End Sub

Of course it will be longer depending on how many checkboxes you have.
You could also add an error handler in case CType(sender, Control).Tag
returned something it shouldn't.
The error handler code would look like this:
Case Else

MessageBox.Show("Error!","Error!",MessageBoxButtons.OK,MessageBoxIcon.Hand,MessageBoxDefaultButton.Button1)
This code would go betwwen the last line inside the Select and End
Select

mediocrevbprogrammer.freed
 
M

Michel Posseth [MCP]

Well Matthew ,
My code is shorter than Stephany Young's:
i am happy you swallowed bether :)

It is a different aproach , with its own pro`s and con`s ( as with all
aproaches :)

However i have one small mod

you would better make label.Text="" to label.Text=string.empty

as string.empty is a constant and doesn`t requir a scan through the string
table ( it is faster , and safer "" vs " " oops )

regards

Michel
 
V

vbnewbie

mediocrevbprogrammer.freed 's code WOULD work if the controls were
created at design time.
Since they are not, I get an error because the label have not been
declared (How could I declare them since they don't exist!)
What would be nice is to have some type of code that would let me
modify or get a value from a control property such as:

tag as a variable (0, 1,2, etc)
lblLabel(tag).Text=""

This is something very usfull in VBA

The other part that does not work in mediocrevbprogrammer.freed 's is
the fact the number of labels that can be created in my app is
limitless. Which means I would need to be able to modify the number of
Cases, or get rid of the Case-End Case all together in favor of a
variable appended to the control name: lblLabel name = lblLabel(tag
number).
Is that at all possible?

Stephany's code worked practically out of the box! Thanks!

A few questions, though.

I am not sure what the undescores stand for.

I am also confused (in this case and others) as to how the Me keyword
works. If you could explain it in this case at least, It would get me
on my way to understand other cases. For example, if you could replace
Me with something else, what would it be?

I understand that your code checks all controls to see if they are
labels and if true checks if the tag number matches. It also searches
if each control found has children etc...
If all the labels and checkboxes where on a panel (say Panel1), what
would you have done differently to simpify the code?

This bit of code has made me understand quite a few things. Thanks!
 
S

Stephany Young

The underscores, e.g. _tag, are nothing more that a convention that I use
for local variable naming.

The Me keyword provides a way to refer to the specific instance of a class
or structure in which the code is currently executing.

In this case Me refers to the instance of the form where the
DynamicCheckBox_CheckChanged event handler is declared and it is used to
pass a reference to the form to the FindControl function, which, by the way,
could be declared in some other class or module outside the form.

The first parameter of the FindControl function is declared as type Control
which means that it will accept any variable that is a reference to an
instance of a class that is derived Control. (Even a Form is derived from
Control.)

The FindControl function is recursive. This means that it continually calls
itself until some condition or other is met. In this case the condition is
that we have found the Label that we are looking for.

Note that when it does call itself, it passes the control that it is
currently dealing with as the first parameter, instead of Me. It is this
taht allows it to 'walk' the 'tree' of controls on the form.

If you knew that all the Labels thate you were interested in were on Panel1,
then you would pass Panel1 as the first parameter instead of Me in the
original call to FindFunction and only the controls that are 'children' of
Panel1 (and their 'chidren') will be inspected.

I didn't notice in your OP that you are using VB.NET 2005. In version 2.0 of
the .NET Framework, (the version used by VB.NET 2005), some nice new methods
were added to a number of classes. One in particular is the
Control.ControlCollection.Find method.

When you reference the Controls property of any control, you are actually
dealing with a ControlCollection object and all it's properties and methods
are automatically made available to you.

The ControlCollection.Find method takes, as it's parameters, a string
representing the name of a control and a boolean indicating whether or not
all child controls should be searched and it returns an array of controls
with 1 element for each control that matches the name parameter.

If we use this method then the whole thing becomes much simpler:

Private Sub DynamicCheckBox_CheckChanged(ByVal sender As Object, ByVal e
As EventArgs)

Dim _tag As Object = CType(sender, Control).Tag

If _tag IsNot Nothing Then
Dim _c As Control() = Controls.Find("lblLabel" & CType(_tag, String),
True)
If _c.Length > 0 Then CType(_c(0), Label).Text = String.Empty
End If

End Sub

and the the FindControl function is no longer required.

Note that:

Dim _c As Control() = Controls.Find("lblLabel" & CType(_tag, String),
True)

and

Dim _c As Control() = Me.Controls.Find("lblLabel" & CType(_tag, String),
True)

are functionally equivalent and the call will search all the control on the
form.

Again, if you know that all the labels of interest are on Panel1 then the
line could read:

Dim _c As Control() = Panel1.Controls.Find("lblLabel" & CType(_tag,
String), True)

which will only search all the control on Panel1.

One thing that you will learn as you go along is that there is often more
then 1 way acheiving something. Some ways may be more efficient that others.
it is a matter of finding what works for you.
 
V

vbnewbie

Well, this time it did not work out of the box...


The problem is with the following line.

If _c.Length > 0 Then CType(_c(0), Label).Text = String.Empty

If I understand correctly, you declared c as Control Array of which the
first and unique element would be the label we are looking for.

However, when it checks for the c.Lengh, it comes up with 0.
If I take that part out and keep:
CType(_c(0), Label).Text = String.Empty
I get an IndexOutOfRangeException

It does find the label though, I checked that.
It does not seem to populate the array...
 
S

Stephany Young

Show us the actual code (copy and paste) that you have inside the event
handler, especially the bit that shows how you determine that the label is
actually found.

The code I posted works for me.
 
V

vbnewbie

Sorry, my mistake...
I had forgoten to change the string part of label names in your code to
match the one I actually use in my code (" lblLabel" as oposed to
"lblFileLabel").

Tried it, it works.

Thanks a million.
 
T

Tom Leylan

Stephany... you've done it according to the requirements but I have a
suggestion.

Avoid tagging the checkbox with an arbitrary numeric value and simply use
the .Tag property to reference the label that is associated with it. Create
the label first, tag it with a number (if these numbers are needed) then
create the checkbox and assign the label reference as the tag. All done.

Tom
 
S

Stephany Young

Good call Tom!

I had been thinking that a better way to do it would have been to use a
Dictionary(Of String, Label) where the key was the name of the relevant
CheckBox control and the value was the Label control.

But that way it still would have required a 'get' of the item of the
dictionay before you could access the Label control.

Your way, the code inside the event handler simply becomes:

CType(CType(sender, Control).Tag, Label).Text = String.Empty

assuming of course that the only controls that are subscribed to the event
handler are the CheckBox controls of interest and that the Tag property for
all of them contains a reference to the appropriate Label.

Just goes to show how easy it is for any of us to get into a mind-set and to
not think outside the square.
 
T

Tom Leylan

Personally I would (probably) go one step further. Considering they are
dynamic I wouldn't create a checkbox with a label but rather generate a
hybrid control that contains a checkbox and a label. That frees up the tag
property and permits me to leverage all the stuff available to a newly
defined class.

Additionally it means we don't walk the controls collection (which may
include non-dynamic checkboxes) trying to recognize the checkboxes we want.
We know we are looking for the hybrid objects. A simple (and separate)
collection of the hybrids makes adding and deleting of the objects easy and
insures we have access to each of them through the collection class.

Rather than writing this in the application:
CType(CType(sender, Control).Tag, Label).Text = String.Empty

one should be able to write:
Control.ClearLabel()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top