How can I respond to events raised by programmatically created UI elements? - vb.net

I'm creating a board game for a piece of coursework. For the board, I'm using some nested For loops running through a 2D array to generate a "Space" object at each square.
The Space object contains a picturebox and some data about that space.
How can I handle events caused by clicking on the generated picturebox without having to hard-code it for each space?
I noticed this question seems to address this, but it's in C# and I couldn't translate it to VB.Net.
Edit:
This is how the board is generated
Dim board(23, 24) As Space
Private Sub GenerateBoard()
Dim spaceSize As New Size(30, 30)
Dim spaceLocation As New Point
Dim validity As Boolean
For Y = 0 To 24
For X = 0 To 23
spaceLocation.X = 6 + (31 * X)
spaceLocation.Y = 6 + (31 * Y)
If validSpaces(Y).Contains(X + 1) Then
validity = True
Else
validity = False
End If
board(X, Y) = New Space(validity, spaceSize, spaceLocation)
Me.Controls.Add(board(X, Y).imageBox)
board(X, Y).imageBox.BackColor = Color.Transparent
board(X, Y).imageBox.BringToFront()
Next
Next
End Sub
Space Class:
Public Class Space
Dim _active As Boolean
Dim _imageBox As PictureBox
Public Sub New(ByVal activeInput As Boolean, ByVal size As Size, ByVal location As Point)
_active = activeInput
_imageBox = New PictureBox
With _imageBox
.Size = size
.Location = location
.Visible = False
End With
End Sub
Property active As Boolean
Get
Return _active
End Get
Set(value As Boolean)
_active = value
End Set
End Property
Property imageBox As PictureBox
Get
Return _imageBox
End Get
Set(value As PictureBox)
_imageBox = value
End Set
End Property
Public Sub highlight()
With _imageBox
.Image = My.Resources.Highlighted_slab
.Visible = True
End With
End Sub
End Class

First all controls created by designer(textbox, label...) a generated by code too, but VisualStudio write this for you. If you open Designer file(yourForm.Designer.vb), then you can see all code how to generate a controls.
If you want a create event handler for your pictureBox , then:
//Initialize control
Private WithEvents _imageBox as PictureBox
Then create a event handler method:
Private Sub imageBox_Click(sender as Object, e as EventArgs)
//Your code
End Sub
Then in VB.NET you can assign a Event handler to the Event in two ways
first: In class constructor after you created a pictureBox( New PictureBox()) add
AddHandler Me._imageBox, AddressOf Me.imageBox_Click
second: On line we you created a event handler add next:
Private Sub imageBox_Click(sender as Object, e as EventArgs) Handles _imageBox.Click
//Your code
End Sub
And remember add your pictureBox to form controls YourForm.Controls.Add(spaceInstance.ImageBox)

Related

How to add infinite components when a button is clicked

I have a social media WinForm. I have a function that basically makes a new picture box when a button is clicked
Public Sub NewPost()
picture as new picturebox
picture.Width = 208
picture.Height = 264
picture.Image = Form2.PictureBox1.Image
picture.Location = New Point(258, 60)
End Sub
The thing is it only generates 1 new picture box because I have to make a new variable each time I want to add a picturebox, and eachtime I have to have a new name. I know my question Is a bit confusing but help would be nice thanks
If you want to trap events for your dynamic PictureBoxes, then you'll have to abandon the WithEvents model and move to using AddHandler.
Here's a quick example where the name of the PictureBox is displayed when it is clicked. Note that I am not setting a Location since they are being added to a FlowLayoutPanel which takes care of the placement for you:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
NewPost()
End Sub
Public Sub NewPost()
Dim picture As New PictureBox
picture.Width = 208
picture.Height = 264
picture.BorderStyle = BorderStyle.FixedSingle
' ...etc...
Dim index As Integer = FlowLayoutPanel1.Controls.Count + 1
picture.Name = "pb" & index
AddHandler picture.Click, AddressOf picture_Click
FlowLayoutPanel1.Controls.Add(picture)
End Sub
Private Sub picture_Click(sender As Object, e As EventArgs)
Dim pb As PictureBox = DirectCast(sender, PictureBox)
Debug.Print(pb.Name)
End Sub
End Class
because I have to make a new variable each time
Not necessarily. You just want to keep a reference to the object. That reference doesn't need to be its own variable, it can just as easily be an element in a list. For example, suppose on your form you have a list of PictureBox objects as a class-level member:
Dim pictureBoxes As New List(Of PictureBox)()
Then in your method you can just add to that list:
Public Sub NewPost()
Dim pictureBox As New PictureBox
pictureBox.Width = 208
pictureBox.Height = 264
pictureBox.Image = Form2.PictureBox1.Image
pictureBox.Location = New Point(258, 60)
Me.pictureBoxes.Add(pictureBox)
End Sub
In this case the pictureBox variable is local to the NewPost method and gets re-created each time. But pictureBoxes is a class-level member and keeps track of the growing list of PictureBox objects that you're creating.
You can use a for while loop to create n number of objects
You can use the existing ControlCollection
Public Function NewPost() As String
Dim picture As New PictureBox
'your code
picture.Name = "Pb" & Form2.Controls.OfType(Of PictureBox).Count
Form2.Controls.Add(picture)
Return picture.Name
End Function
then you can retrive it
DirectCast(Form2.Controls(NewPost), PictureBox).Image = Form2.PictureBox1.Image
'OR
DirectCast(Form2.Controls("Pb12"), PictureBox).Image = Form2.PictureBox1.Image

How do I code out with numeric keypads with multi textboxes?

I'm trying to code out a programme where the user sees a form and in that form, there are 2 text boxes and 10 buttons.
Username:
Password:
1 2 3
4 5 6
7 8 9
0
I've tried this code
Private Sub Btn1_Click(sender As Object, e As EventArgs) Handles Btn1.Click
If UsernameTextbox.Focused = True Then
UsernameTextbox.Text = UsernameTextbox.Text + "1"
End If
End Sub
I understand that clicking on Btn1 will steal the focus from the text box. So how can I write the programme?
One option would be to declare a variable of type Control and, in the Leave event handler for each control, assign the sender to that variable. You can then use that variable in the Click event handler of your Button to determine which control had focus and possibly reassign back to that control and then update it appropriately. You can do the lot with two event handlers, e.g.
Private previouslyActiveTextBox As TextBox
Private Sub TextBoxes_Leave(sender As Object, e As EventArgs) Handles TextBox2.Leave,
TextBox1.Leave
previouslyActiveTextBox = DirectCast(sender, TextBox)
End Sub
Private Sub Buttons_Click(sender As Object, e As EventArgs) Handles Button3.Click,
Button2.Click,
Button1.Click
previouslyActiveTextBox.Select()
previouslyActiveTextBox.SelectedText = CStr(DirectCast(sender, Button).Tag)
End Sub
That code handles multiple events with a single method in both cases. It also requires that you assign the number for each Button to the Tag property of that control. Note that it also sets the SelectedText, rather than appending to the Text property. That is more correct because it will add the new text where the caret is actually located and replace text if it is selected.
An even better option might be to use a custom button control that doesn't take focus. Here's one I prepared earlier:
http://www.vbforums.com/showthread.php?459890-Building-Blocks-for-an-On-screen-Keyboard
Items within a ToolStrip do not grab focus when clicked. While the standard ToolStrip usage is as a menu bar, there is nothing that prevents you from using it as a container for buttons laid out in a grid. In fact, the class ToolStrip.LayoutStyle Property allows you select a table style.
The following is a proof-of-concept custom ToolStrip that is prepopulated with the buttons to create a number pad like control. The control has sufficient function to work as intended, but is not locked down to prevent misuse by manipulating the Items collection and other properties.
Public Class NumPadToolstrip : Inherits ToolStrip
Private _ButtonSize As Size = New Size(50, 50)
Private _ButtonMargin As Padding = New Padding(5)
Private _ButtonBackColor As Color = Color.Ivory
Public Sub New()
MyBase.New
LayoutStyle = ToolStripLayoutStyle.Table
Dim settings As TableLayoutSettings = CType(LayoutSettings, TableLayoutSettings)
settings.ColumnCount = 3
settings.RowCount = 4
AddButtons(7, 9)
AddButtons(4, 6)
AddButtons(1, 3)
AddButtons(0, 0)
Dock = DockStyle.None
AutoSize = True
BackColor = Color.LightGray
End Sub
Public Property ButtonSize As Size
Get
Return _ButtonSize
End Get
Set(value As Size)
If value <> _ButtonSize Then
_ButtonSize = value
UpdateButtonSizes()
End If
End Set
End Property
Public Property ButtonMargin As Padding
Get
Return _ButtonMargin
End Get
Set(value As Padding)
If value <> _ButtonMargin Then
_ButtonMargin = value
UpdateMargins()
End If
End Set
End Property
Public Property ButtonBackColor As Color
Get
Return _ButtonBackColor
End Get
Set(value As Color)
If value <> _ButtonBackColor Then
_ButtonBackColor = value
UpdateButtonBackColor()
End If
End Set
End Property
Private Sub AddButtons(start As Int32, [end] As Int32)
For num As Int32 = start To [end]
Dim b As New ToolStripButton With {.Text = num.ToString(),
.Size = ButtonSize,
.Margin = ButtonMargin,
.BackColor = ButtonBackColor,
.AutoSize = False}
AddHandler b.Paint, Sub(sender As Object, e As PaintEventArgs)
With e.Graphics
Dim r As Rectangle = e.ClipRectangle
r.Inflate(-1, -1)
r.Location = Point.Empty
.DrawRectangle(Pens.Black, r)
End With
End Sub
Items.Add(b)
Next
End Sub
Private Sub UpdateButtonSizes()
SuspendLayout()
For Each btn As ToolStripButton In Items.OfType(Of ToolStripButton)
btn.Size = _ButtonSize
Next
ResumeLayout()
End Sub
Private Sub UpdateMargins()
SuspendLayout()
For Each btn As ToolStripButton In Items.OfType(Of ToolStripButton)
btn.Margin = _ButtonMargin
Next
ResumeLayout()
End Sub
Private Sub UpdateButtonBackColor()
SuspendLayout()
For Each btn As ToolStripButton In Items.OfType(Of ToolStripButton)
btn.BackColor = _ButtonBackColor
Next
ResumeLayout()
End Sub
End Class
Add the above class to your project and perform a build operation. The NumPadToolstrip control should then be available in the ToolBox. Add the control to the form and then add a handler for its ItemClicked event to pass the proper text to the TextBox.
Private Sub NumPadToolstrip1_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles NumPadToolstrip1.ItemClicked
Dim tb As TextBoxBase = TryCast(ActiveControl, TextBoxBase)
If tb IsNot Nothing Then tb.SelectedText = e.ClickedItem.Text
End Sub

How to make a fixed background on a DataGridView

I made a custom control that inherits DataGridView in order to have a transparent background. Now I am trying to set up an scroll feature on a timer that scrolls down one row every second. However, when I try to scroll (vertically), the background image is not fixed. Is there any way to make the background image fixed when scrolling?
EDIT: Here is the code for handling the scroll timer.
Private Sub Sub1
'Some previous code
If DataGridView1.Rows.Count > 10 Then
ScrollIndex1 = 0 'Integer for scroll index
DGVAutoScroll()
End If
End Sub
Private Sub DGVAutoScroll()
Timer2.Enabled = True
Timer2.Interval = 1000
End Sub
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
If ScrollIndex1 = DataGridView1.Rows.Count - 1 Then
ScrollIndex1 = 0
DataGridView1.FirstDisplayedScrollingRowIndex = ScrollIndex1
ScrollIndex1 += 1
Else
DataGridView1.FirstDisplayedScrollingRowIndex = ScrollIndex1
ScrollIndex1 += 1
End If
End Sub
'Custom DataGridView class
Imports System.ComponentModel
Imports System.Windows.Forms
Public Class MyDGV
Inherits DataGridView
Public Property DGVHasTransparentBackground As Boolean
Get
Return Nothing
End Get
Set()
SetTransparentProperties(True)
End Set
End Property
Public Property ScrollBar
Get
Return Nothing
End Get
Set(value)
BackgroundColor = Color.Transparent
End Set
End Property
Public Sub New()
DGVHasTransparentBackground = True
End Sub
Private Sub SetTransparentProperties(ByRef SetAsTransparent As Boolean)
MyBase.DoubleBuffered = True
MyBase.EnableHeadersVisualStyles = False
MyBase.ColumnHeadersDefaultCellStyle.BackColor = Color.Transparent
MyBase.RowHeadersDefaultCellStyle.BackColor = Color.Transparent
SetCellStyle(Color.Transparent)
End Sub
Protected Overrides Sub PaintBackground(graphics As System.Drawing.Graphics, clipBounds As System.Drawing.Rectangle, gridBounds As System.Drawing.Rectangle)
MyBase.PaintBackground(graphics, clipBounds, gridBounds)
Dim rectSource As New Rectangle(MyBase.Location, MyBase.Size)
Dim rectDest As New Rectangle(0, 0, rectSource.Width, rectSource.Height)
Dim b As New Bitmap(Parent.ClientRectangle.Width, Parent.ClientRectangle.Height)
Graphics.FromImage(b).DrawImage(MyBase.Parent.BackgroundImage, Parent.ClientRectangle)
graphics.DrawImage(b, rectDest, rectSource, GraphicsUnit.Pixel)
End Sub
Protected Overrides Sub OnColumnAdded(e As System.Windows.Forms.DataGridViewColumnEventArgs)
MyBase.OnColumnAdded(e)
SetCellStyle(Color.Transparent)
End Sub
Private Sub SetCellStyle(ByVal cellColour As Color)
For Each col As DataGridViewColumn In MyBase.Columns
col.DefaultCellStyle.BackColor = cellColour
col.DefaultCellStyle.SelectionBackColor = cellColour
Next
End Sub
End Class
It looks like I had to call DataGridView1.SelectAll() within the timer tick. Thanks everyone.
I assume you want an image behind data (datagridview).
If datagridview is transparent then just add an image in the background. Stick it on the form or on an item behind datagridview. It will no scroll since it would be outside datagridview
Change image alpha if data not clearly visible.

New Multi-threading not working

I'm trying to create a thread so when I click a button it creates a new PictureBox from a class, this is how far I've got but nothing comes up on the screen at all.
Form1 code:
Public Class Form1
Private pgClass As New SecondUIClass
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
pgClass = New SecondUIClass
pgClass.x += 100
pgClass.thread()
End Sub
End Class
Class Code:
Imports System.Threading
Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private trd As Thread
Public Sub thread()
trd = New Thread(AddressOf NewUIThread)
trd.SetApartmentState(ApartmentState.STA)
trd.IsBackground = False
trd.Start()
End Sub
Private Sub NewUIThread()
emeny(counter) = New PictureBox
emeny(counter).BackColor = Color.Red
emeny(counter).Visible = True
emeny(counter).Location = New System.Drawing.Point(x, 100)
emeny(counter).Size = New System.Drawing.Size(10, 50)
Form1.Controls.Add(emeny(counter))
For z = 0 To 13
emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
Application.DoEvents()
Threading.Thread.Sleep(100)
Next
counter += 1
End Sub
End Class
I have posted something similar before on here but it was different, the pictureBoxes were showing on the screen but I was trying to get them to move at the same time but they wouldn't move, they only moved one at a time. The question that I asked before was this Multi threading classes not working correctly
I made a few assumptions for this answer so it may not work for you out of the box but I think it will put you on the right track without using any Thread.Sleep calls because I personally don't like building intentional slows to my apps but that's a personal preference really.
So For my example I just used a bunch of textboxes because I didn't have any pictures handy to fiddle with. But basically to get it so that the user can still interact with the program while the moving is happening I used a background worker thread that is started by the user and once its started it moves the textboxes down the form until the user tells it to stop or it hits an arbitrary boundary that I made up. So in theory the start would be the space bar in your app and my stop would be adding another control to the collection. For your stuff you will want to lock the collection before you add anything and while you are updating the positions but that is up to your discretion.
So the meat and potatoes:
in the designer of the form I had three buttons, btnGo, btnStop and btnReset. The code below handles the click event on those buttons so you will need to create those before this will work.
Public Class Move_Test
'Flag to tell the program whether to continue or to stop the textboxes where they are at that moment.
Private blnStop As Boolean = False
'Worker to do all the calculations in the background
Private WithEvents bgWorker As System.ComponentModel.BackgroundWorker
'Controls to be moved.
Private lstTextBoxes As List(Of TextBox)
'Dictionary to hold the y positions of the textboxes.
Private dtnPositions As Dictionary(Of Integer, Integer)
Public Sub New()
' Default code. Must be present for VB.NET forms when overwriting the default constructor.
InitializeComponent()
' Here I instantiate all the pieces. The background worker to do the adjustments to the position collection, the list of textboxes to be placed and moved around the form
' and the dictionary of positions to be used by the background worker thread and UI thread to move the textboxes(because in VB.NET you can not adjust controls created on the UI thread from a background thread.
bgWorker = New System.ComponentModel.BackgroundWorker()
Me.lstTextBoxes = New List(Of TextBox)
Me.dtnPositions = New Dictionary(Of Integer, Integer)
For i As Integer = 0 To 10
Dim t As New TextBox()
t.Name = "txt" & i
t.Text = "Textbox " & i
'I used the tag to hold the ID of the textbox that coorelated to the correct position in the dictionary,
' technically you could use the same position for all of them for this example but if you want to make the things move at different speeds
' you will need to keep track of each individually and this would allow you to do it.
t.Tag = i
dtnPositions.Add(i, 10)
'Dynamically position the controls on the form, I used 9 textboxes so i spaced them evenly across the form(divide by 10 to account for the width of the 9th text box).
t.Location = New System.Drawing.Point(((Me.Size.Width / 10) * i) + 10, dtnPositions(i))
Me.lstTextBoxes.Add(t)
Next
'This just adds the controls to the form dynamically
For Each r In Me.lstTextBoxes
Me.Controls.Add(r)
Next
End Sub
Private Sub Move_Test_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
'Don't need to do anything here. Placeholder
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Try
If Not bgWorker.IsBusy Then
'User starts the movement.
bgWorker.RunWorkerAsync()
End If
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click
Try
'Reset the positions and everything else on the form for the next time through
' I don't set the blnStop value to true in here because it looked cooler to keep reseting the textboxes
' and have them jump to the top of the form and keep scrolling on their own...
For Each r In Me.lstTextBoxes
r.Location = New System.Drawing.Point(r.Location.X, 10)
Next
For i As Integer = 0 To dtnPositions.Count - 1
dtnPositions(i) = 10
Next
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub bgWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
Try
'This is where we do all the work.
' For this test app all its doing is scrolling through each value in the dictionary and incrementing the value
' You could make the dictionary hold a custom class and have them throttle themselves using variables on the class(or maybe travel at an angle?)
For i As Integer = 0 To dtnPositions.Count - 1
dtnPositions(i) += 1
Next
Catch ex As Exception
blnStop = True
End Try
End Sub
Private Sub bgWorker_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
Try
'Once the background worker is done updating the positions this function scrolls through the textboxes and assigns them their new positions.
' We have to do it in this event because we don't have access to the textboxes on the backgroun thread.
For Each r In Me.lstTextBoxes
r.Location = New System.Drawing.Point(r.Location.X, dtnPositions(CInt(r.Tag)))
Next
'use linq to find any textboxes whose position is beyond the threshhold that signifies they are down far enough.
' I chose the number 100 arbitrarily but it could really be anything.
Dim temp = From r In Me.lstTextBoxes Where r.Location.Y > (Me.Size.Height - 100)
'If we found any textboxes beyond our threshold then we set the top boolean
If temp IsNot Nothing AndAlso temp.Count > 0 Then
Me.blnStop = True
End If
'If we don't want to stop yet we fire off the background worker again and let the code go otherwise we set the stop boolean to false without firing the background worker
' so we will be all set to reset and go again if the user clicks those buttons.
If Not Me.blnStop Then
bgWorker.RunWorkerAsync()
Else
Me.blnStop = False
End If
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
Try
'The user clicked the stop button so we set the boolean and let the bgWorker_RunWorkerCompleted handle the rest.
Me.blnStop = True
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
End Class
Theres a lot of code there but a lot of it is comments and I tried to be as clear as possible so they are probably a little long winded. But you should be able to plop that code on a new form and it would work without any changes. I had the form size quite large (1166 x 633). So I think that's when it works best but any size should work(smaller forms will just be more cluttered).
Let me know if this doesn't work for your application.
This is a problem that is well suited to async/await. Await allows you to pause your code to handle other events for a specific period of time..
Private Async Function Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) As Task Handles Button1.Click
pgClass = New SecondUIClass
pgClass.x += 100
await pgClass.NewUIThread()
End Sub
End Class
Class Code:
Imports System.Threading
Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private Async Function NewUIThread() As Task
emeny(counter) = New PictureBox
emeny(counter).BackColor = Color.Red
emeny(counter).Visible = True
emeny(counter).Location = New System.Drawing.Point(x, 100)
emeny(counter).Size = New System.Drawing.Size(10, 50)
Form1.Controls.Add(emeny(counter))
For z = 0 To 13
emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
await Task.Delay(100) 'The await state machine pauses your code here in a similar way to application.doevents() until the sleep has completed.
Next
counter += 1
End Sub
End Class

Programmatically add controls to form

I'm using the attached code to add another line\row of controls beneath an existing set (when a label is clicked). There could be quite a few rows added so I'm having to repeat the code many times using the counter (i) to keep track...
Is there a better method for doing this?
Private Sub Label10_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LblExpandSearch.Click
If i = 0 Then
'TextBox7
'
Dim TextBox7 As New TextBox
TextBox7.Size = New Size(302, 20)
TextBox7.Name = "TextBox7"
TextBox7.Location = New System.Drawing.Point(60, 135)
Me.ExpAdvancedSearch.Controls.Add(TextBox7)
'RadioButton5
'
Dim RadioButton5 As New RadioButton
RadioButton5.AutoSize = True
RadioButton5.Checked = True
RadioButton5.Location = New System.Drawing.Point(77, 112)
RadioButton5.Name = "RadioButton5"
RadioButton5.Size = New System.Drawing.Size(55, 17)
RadioButton5.TabIndex = 48
RadioButton5.TabStop = True
RadioButton5.Text = "NEAR"
RadioButton5.UseVisualStyleBackColor = True
ElseIf i = 1 Then
ExpAdvancedSearch.Size_ExpandedHeight = 260
'TextBox8
'
Dim TextBox8 As New TextBox
TextBox8.Size = New Size(302, 20)
TextBox8.Name = "TextBox8"
TextBox8.Location = New System.Drawing.Point(60, 185)
Me.ExpAdvancedSearch.Controls.Add(TextBox8)
'RadioButton9
'
Dim RadioButton9 As New RadioButton
RadioButton9.AutoSize = True
RadioButton9.Checked = True
RadioButton9.Location = New System.Drawing.Point(77, 162)
RadioButton9.Name = "RadioButton9"
RadioButton9.Size = New System.Drawing.Size(55, 17)
RadioButton9.TabIndex = 48
RadioButton9.TabStop = True
RadioButton9.Text = "NEAR"
RadioButton9.UseVisualStyleBackColor = True
End If
i = i + 1
End Sub
Hmmm.. UseVisualStyleBackColor says 'winforms' to me.
A few points...
Don't add controls all to one panel, use a usercontrol.
Then just add instances of that.
Don't process click events from a label
Use a linklabel or button. Anything else = being mean to users. Of course it makes sense to you, you thought of it! Now so with users, this is black and white.
Sample...
Very minimal of course. You'll want to:
Put the items in a scrollable panel instead of right on the form.
Add them to a generic list of uc probably, too.
Set form's min/max size - to allow reasonable sizing (allow any height > ~100)
Set uc's and controls .Anchor properties to allow reasonable resizing
uc.vb
Public Class uc
Inherits System.Windows.Forms.UserControl
Private components As System.ComponentModel.IContainer
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
Friend WithEvents LinkLabel1 As System.Windows.Forms.LinkLabel
Public Sub New()
MyBase.New()
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.LinkLabel1 = New System.Windows.Forms.LinkLabel
Me.SuspendLayout()
Me.TextBox1.Location = New System.Drawing.Point(8, 8)
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.Size = New System.Drawing.Size(88, 20)
Me.TextBox1.TabIndex = 0
Me.TextBox1.Text = "TextBox1"
Me.LinkLabel1.Enabled = False
Me.LinkLabel1.Location = New System.Drawing.Point(112, 8)
Me.LinkLabel1.Name = "LinkLabel1"
Me.LinkLabel1.Size = New System.Drawing.Size(24, 16)
Me.LinkLabel1.TabIndex = 1
Me.LinkLabel1.TabStop = True
Me.LinkLabel1.Text = "add"
Me.Controls.Add(Me.LinkLabel1)
Me.Controls.Add(Me.TextBox1)
Me.Name = "uc"
Me.Size = New System.Drawing.Size(148, 36)
Me.ResumeLayout(False)
End Sub
Private _addcallback As EventHandler = Nothing
Public Property AddCallback() As EventHandler
Get
Return _addcallback
End Get
Set(ByVal Value As EventHandler)
_addcallback = Value
LinkLabel1.Enabled = Not Value Is Nothing
End Set
End Property
Private Sub LinkLabel1_LinkClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles LinkLabel1.LinkClicked
If AddCallback Is Nothing Then Throw New ApplicationException("AddCallback not set on a uc") ' ALWAYS check for errors like this
_addcallback(Me, Nothing)
AddCallback = Nothing ' gray myself out, can't insert in thie implementation
End Sub
End Class
frm.vb
Public Class frm
Inherits System.Windows.Forms.Form
Private components As System.ComponentModel.IContainer
Public Sub New()
MyBase.New()
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 266)
Me.Name = "Form1"
Me.Text = "Form1"
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddClicked(Me, Nothing)
End Sub
Private Sub AddClicked(ByVal sender As Object, ByVal e As System.EventArgs)
Dim myuc As New uc
myuc.AddCallback = AddressOf AddClicked
If Controls.Count > 0 Then
myuc.Top = Controls(Controls.Count - 1).Bottom
End If
Me.Controls.Add(myuc)
End Sub
End Class
I don't know if there's a "less code" approach to this but I do know that you can save your fingers using a With statement.
Dim RadioButton5 As New RadioButton
With RadioButton5
.AutoSize = True
.Checked = True
.Location = New System.Drawing.Point(77, 112)
.Name = "RadioButton5"
.Size = New System.Drawing.Size(55, 17)
.TabIndex = 48
.TabStop = True
.Text = "NEAR"
.UseVisualStyleBackColor = True
End With
If you need to add an indefinite number of items to a single page, then you need to store those items in an array list that we can later add to the page dynamically.
Imports System.Collections.Generic
Partial Class Default2
Inherits System.Web.UI.Page
''# the i integer is here for helping to set the ID of the radio button
''# as well as the tabindex
Private Shared _i As Integer
Public Shared Property i As Integer
Get
Return _i
End Get
Set(ByVal value As Integer)
_i = value
End Set
End Property
''# we need to create an array of our control list class
Public Shared _ctrlList As List(Of ControlList)
''# page load event
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
''# if the page is not a postback, then we need to initialize the Control List
_ctrlList = New List(Of ControlList)
i = 0
End If
End Sub
''# button click event
Protected Sub button_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button.Click
''# create a new RadioButton every time the button is clicked
Dim rb As RadioButton = New RadioButton
With rb
.ID = "radioButton" + i.ToString
.Checked = True
.TabIndex = 48 + i
.Text = "NEAR"
End With
''# create a new literal every time the button is clicked
Dim lt As Literal = New Literal
With lt
.ID = "literal" + i.ToString
.Text = " <strong>my fancy text</strong><br />"
End With
''# add the radio button and literal to our custom array
_ctrlList.Add(New ControlList(rb, lt))
''# loop through the array and add the controls to the page
For Each cl In _ctrlList
LabelPlaceHolder.Controls.Add(cl.RadioBtn)
LabelPlaceHolder.Controls.Add(cl.Litrl)
Next
''# increment the i counter so that we have unique radioButton ID's
i = i + 1
End Sub
''# this is our custom Control List
''# the idea behind this is for us to store
''# an array of Radio Buttons and literals to
''# spit out onto the page
''# NOTE: you can add as many controls as you like
''# to this list and even add static "Literals" to
''# help you with your formatting (IE: DIV tags or <BR> tags
Public Class ControlList
Private _RadioBtn As RadioButton
Public Property RadioBtn As RadioButton
Get
Return _RadioBtn
End Get
Set(ByVal value As RadioButton)
_RadioBtn = value
End Set
End Property
Private _Litrl As Literal
Public Property Litrl As Literal
Get
Return _Litrl
End Get
Set(ByVal value As Literal)
_Litrl = value
End Set
End Property
Public Sub New(ByVal radioBtn As RadioButton, ByVal litrl As Literal)
_RadioBtn = radioBtn
_Litrl = litrl
End Sub
End Class
End Class
Try this and see how it works. All you need in your ASPX is
<form id="form1" runat="server">
<asp:PlaceHolder runat="server" id="LabelPlaceHolder" /><br />
<asp:Button ID="button" runat="server" Text="click me" />
</form>
Basically what this does is add an additional control set to the page every time the button is clicked. You can have an indefinite number of controls on the page without adding any additional code.