Multi-Dimentional Control Arrays And A Problem

  • Thread starter Douglas E. Rasmusen
  • Start date
D

Douglas E. Rasmusen

Copy and paste this into the code of a Windows form...
'Written by Douglas E. Rasmusen ([email protected])

'Create a form named Form1.

Option Strict Off

Option Explicit On

Friend Class Form1

Inherits System.Windows.Forms.Form

Dim TextCounter As Integer

Private Sub Form1_Click(ByVal eventSender As System.Object, ByVal eventArgs
As System.EventArgs) Handles MyBase.Click

TextCounter += 1 'Increase the counter for identification purposes.

Dim txtText As New TextBox ' Declare a new TextBox.

Me.Controls.Add(txtText) ' Add the TextBox to the form's Controls
collection.

With txtText

..Top = 20 * TextCounter 'Offset the TextBox vertically.

..Left = 20 * TextCounter 'Offset the TextBox horizontally.

..Width = 50 'Set the TextBox's width.

..Visible = True 'Make it seen by the user.

..Tag = Str(TextCounter) 'The Tag property acts as the old "Index" property
(e.g. TextCounter = 4, Tag = " 4").

'This method allows for multidimentional control arrays

'limited only by the length of the Tag property.

'Example: txtText.Tag = " 1024 23 99" could represent a TextBox on the
1024th row,

'the 23rd column, and 99 pages deep.

..Text = txtText.Tag 'Display the Tag property to the user.

AddHandler txtText.DoubleClick, AddressOf txtText_DoubleClick 'Add a
DoubleClick event.

End With

End Sub


Private Sub txtText_DoubleClick(ByVal sender As Object, ByVal e As
System.EventArgs)

Dim answer As String 'Declare a variable for the user's input.

MessageBox.Show("You seleced TextBox Number" & sender.tag) 'Tells the user
which TextBox was selected.

answer = InputBox("Which TextBox would you like to destroy? Enter 1 through"
& Str(TextCounter) & " or ALL") 'There is no error checking here.

If answer = "ALL" Then

For Each txtText As TextBox In Me.Controls

Me.Controls.Remove(txtText)

Next

Else

For Each txtText As TextBox In Me.Controls 'Loop through all the TextBoxes
on the form.

MessageBox.Show("Checking txtText number" & txtText.Tag) 'Shows user which
TextBox is currently being examined.

If txtText.Tag = " " & answer Then 'Note the additional space before the
answer.

Me.Controls.Remove(txtText) 'Removes the selected control.

End If

Next

End If

End Sub

End Class



It works, but if you try the ALL option, it only removes the odd numbered
TextBoxes?!?!?! Any ideas or coments?
 
A

Armin Zingler

Am 18.02.2011 07:06, schrieb Douglas E. Rasmusen:
Copy and paste this into the code of a Windows form...
'Written by Douglas E. Rasmusen ([email protected])

'Create a form named Form1.

Option Strict Off

Option Strict On

There is no prudential reason to switch off compiler
checks, opening the floodgates to errors the compiler
would be able to recognize otherwise. It's a little
more to type but safe data handling and avoiding
late binding comes first.

Usually I don't reply to a posting containg that LOC. ;)

For Each txtText As TextBox In Me.Controls

Me.Controls.Remove(txtText)

Next

Replace code above by:

Controls.Clear

Me must not remove items from a collection on which
an enumerator is currently in use. If you remove
one item and the enumerator advances also, it
always skips one item.

Alternative #1:
do until controls.count = 0
controls.removeat(0)
loop

Alternative #1:
for index = controls.count - 1 to 0 step -1
controls.removeat(index)
next


In addition, _if_ you only want to remove the Textboxes,
this is not done just by declaring the iteration variable
As Textbox. It still processes all items in the Controls
collection. If one of the items is not a Textbox,
the assignment to the iteration variable causes an
exception. Code to delete only the Textboxes:

Dim q = From ctl In Controls.Cast(Of Control)() _
Where TypeOf ctl Is TextBox
q.ToList.ForEach(AddressOf Controls.Remove)

(<thinking>Why does IEnumerable(Of) not have a ForEach method?
Well, an extension method helps.</thinking>)

Or the longer version (doing the same):
Dim index As Integer

Do Until index = Controls.Count
Dim ctl = Controls(index)

If TypeOf ctl Is TextBox Then
Controls.RemoveAt(index)
Else
index += 1
End If
Loop
 
D

Douglas E. Rasmusen

Armin,

Thank you in advance for the fast response! I have turned on Option Strict,
and have four errors to address. I'll post my progress. What do you think
of the idea of using the tag property as an index?

-Doug

P.S. I am considering a vb.net online user group where projects,
techniques, resources, and ideas could be shared and discussed. I know that
sounds redundant, but darned if I can't find a single place that has
downloadable projects, samples, and templates.
 
D

Douglas E. Rasmusen

Armin,

Dim selected As String = Str(sender.tag)

This comes back wtih a 'late binding' error. Can you explain this? Thank
you!

P.S. Your long method for removing only the TextBozes worked! Thanks!

-Doug
 
A

Armin Zingler

Am 19.02.2011 01:25, schrieb Douglas E. Rasmusen:
What do you think
of the idea of using the tag property as an index?

Yes, a possible solution. If you'll need more additional
values for each Textbox, I'd suggest to inherit from
Textbox and add the missing properties.
 
A

Armin Zingler

Am 19.02.2011 02:11, schrieb Douglas E. Rasmusen:
Armin,

Dim selected As String = Str(sender.tag)

This comes back wtih a 'late binding' error. Can you explain this? Thank
you!

I can't repro a late binding error. The error I get is

"Option Strict On disallows implicit conversions from '<type1>' to '<type2>' "

Do you mean this? If yes:
(code in [] = optional with Option Infer On)


Dim ctl [As Control] = directcast(sender, control) '
'...

Dim selected [As String] = ctl.tag.ToString

- or -

Dim selected [As String] = directcast(sender.tag, string)

If I know that Tag is always a String, I prefer the former because
it's a little shorter and no casting is necessary.


P.S. Your long method for removing only the TextBozes worked! Thanks!

The short one didn't? I have switched Option Infer On (using VB 2008).
If it's an upgraded project, required references and imports may be
missing for LINQ to work.
 
D

Douglas E. Rasmusen

Thank you for the fix, however, for some mysterious reason, the remove code
has stopped working. I am using 2008 Express. Does that have anything to
do with it?

For Each txtText As TextBox In Me.Controls 'Loop through all the TextBoxes
on the form.

MessageBox.Show("Checking txtText number" & Str(txtText.Tag)) 'Shows user
which TextBox is currently being examined.

If txtText.Tag Is " " & answer Then 'Note the additional space before the
answer.

MsgBox("Trying to remove...")

Me.Controls.Remove(txtText) 'Removes the selected control.

End If

Next

End If

I would still like to hear your opinion on using the tag property as an
index.


Armin Zingler said:
Am 19.02.2011 02:11, schrieb Douglas E. Rasmusen:
Armin,

Dim selected As String = Str(sender.tag)

This comes back wtih a 'late binding' error. Can you explain this?
Thank
you!

I can't repro a late binding error. The error I get is

"Option Strict On disallows implicit conversions from '<type1>' to
'<type2>' "

Do you mean this? If yes:
(code in [] = optional with Option Infer On)


Dim ctl [As Control] = directcast(sender, control) '
'...

Dim selected [As String] = ctl.tag.ToString

- or -

Dim selected [As String] = directcast(sender.tag, string)

If I know that Tag is always a String, I prefer the former because
it's a little shorter and no casting is necessary.


P.S. Your long method for removing only the TextBozes worked! Thanks!

The short one didn't? I have switched Option Infer On (using VB 2008).
If it's an upgraded project, required references and imports may be
missing for LINQ to work.
 
A

Armin Zingler

Am 19.02.2011 17:09, schrieb Douglas E. Rasmusen:
Thank you for the fix, however, for some mysterious reason, the remove code
has stopped working. I am using 2008 Express. Does that have anything to
do with it?

No. When the application runs, it doesn't know which IDE has been
used to produce the code. You can write it in Notepad, too.
For Each txtText As TextBox In Me.Controls 'Loop through all the TextBoxes
on the form.

MessageBox.Show("Checking txtText number" & Str(txtText.Tag)) 'Shows user
which TextBox is currently being examined.

If txtText.Tag Is " " & answer Then 'Note the additional space before the
answer.

MsgBox("Trying to remove...")

Me.Controls.Remove(txtText) 'Removes the selected control.

End If

Next

End If

Isn't this the _old_ code again using "For Each"? It still can't work
for the reason already explained. My version (the long one) to remove
the Textboxes only was using Do-Loop.

The Is-Operator doesn't make sense here. Use "=" to compare Strings.
I would still like to hear your opinion on using the tag property as an
index.

I've already replied in the other post. :)
 
A

Armin Zingler

Am 19.02.2011 17:09, schrieb Douglas E. Rasmusen:
Thank you for the fix, however, for some mysterious reason, the remove code
has stopped working. I am using 2008 Express. Does that have anything to
do with it?

For Each txtText As TextBox In Me.Controls 'Loop through all the TextBoxes
on the form.

MessageBox.Show("Checking txtText number" & Str(txtText.Tag)) 'Shows user
which TextBox is currently being examined.

If txtText.Tag Is " " & answer Then 'Note the additional space before the
answer.

MsgBox("Trying to remove...")

Me.Controls.Remove(txtText) 'Removes the selected control.

End If

Next

End If

I didn't read it thoroughly enough before my last reply. So, discard it.

First I'd get rid of the blank by not using the Str function anymore.
So, when you store the index in a Textbox, either store the Integer
value itself without a String conversion, or use the ToString function
instead. Str is plain old and there is no good reason to use it.
As you see, the blank isn't required.

If the purpose of the code is to delete a Textbox with a certain index,
I'd write it this way:

For Each txtText As TextBox In Me.Controls
If txtText.Tag.ToString = answer Then
Me.Controls.Remove(txtText) 'Removes the selected control.
Exit For
End If
Next

But again, the declaration "As Textbox" does _not_ select a subset
of all Controls. The loop still iterates on all controls (including
buttons etc), and the loop will fail because other control types
can not be assigend to txtText. Hence...:

For Each ctl In Me.Controls 'Loop through all Controls
If TypeOf ctl Is TextBox Then
If txttext.Tag.ToString = answer Then
Me.Controls.Remove(txttext) 'Removes the selected control.
Exit For
End If
End If
Next


To be hoenst, I wouldn't use any of these approaches. Instead, as you
are dynamically the Textboxes, you can store them in an array or List(Of).
Later you don't have to search for the Textboxes; just use the index
instead. The whole code rewritten then looks like this:

Option Strict On
Option Infer On

Public Class Form1
Private ReadOnly TextBoxes As New List(Of TextBox)

Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click

Dim Index = TextBoxes.Count + 1
Dim txt As New TextBox

txt.SetBounds(20 * Index, 20 * Index, 50, txt.Height)
AddHandler txt.DoubleClick, AddressOf txtText_DoubleClick
TextBoxes.Add(txt)
Controls.Add(txt)

End Sub
Private Sub txtText_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs)

Dim answer As String
Dim txt = DirectCast(sender, TextBox)
Dim Index = TextBoxes.IndexOf(txt)

MessageBox.Show("You seleced TextBox Number " & (Index + 1))

answer = InputBox("Which TextBox would you like to destroy? Enter 1 through" _
& TextBoxes.Count & " or ALL")

If answer = "ALL" Then
TextBoxes.ForEach(AddressOf Controls.Remove)
TextBoxes.Clear()
Else 'no input checking here
Dim DeleteIndex = Integer.Parse(answer) - 1

Controls.Remove(TextBoxes(DeleteIndex))
TextBoxes(DeleteIndex) = Nothing
End If

End Sub

End Class

I guess it's a training project, so I don't challenge it's content. :)
 

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