VB.net nearest control - vb.net

Is there an easy way to get the nearest control to a control of choice?
I have a picture box and some other moving controls. I want to delete the nearest control to my picture box.
So I have to get the position of all controls and delete that with the Location nearest to the Location of my picture box. I'm not sure about how to do that the best way.

If "closest" in your case indeed means "with the Location nearest to the Location of my picture box", then the easiest would be:
Me.Controls.Remove((From c In Me.Controls.Cast(Of Control)() Order By c.Location.Distance(PictureBox1.Location) Select c).Skip(1).Take(1)(0))
, where Distance is defined in a module like this:
<System.Runtime.CompilerServices.Extension()> _
Public Function Distance(ByVal p1 As Point, ByVal p2 As Point) As Integer
Return (p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y)
End Function

Here's an entire sample form that calculates "closest" by checking if the supplied control is in 1 of 8 regions relative to the base control. Half of the code is setup trying to mimic the scenario you described. The MainButton_Click and below is the meat of the work.
Option Explicit On
Option Strict On
Public Class Form1
Private MainPB As PictureBox
Private OtherPB As List(Of PictureBox)
Private WithEvents MainButton As Button
Private Rnd As New Random()
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Setup the form with sample data
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
Me.MainButton = New Button()
Me.MainButton.Text = "Update"
Me.MainButton.Left = 1
Me.MainButton.Top = 20
Me.Controls.Add(Me.MainButton)
Me.Width = 1000
Me.Height = 1000
MainPB = New PictureBox()
MainPB.BackColor = Color.Red
MainPB.Width = 100
MainPB.Height = 100
MainPB.Left = (Me.Width \ 2) - (MainPB.Width \ 2)
MainPB.Top = (Me.Height \ 2) - (MainPB.Height \ 2)
Me.Controls.Add(MainPB)
Me.OtherPB = New List(Of PictureBox)
For I = 0 To 50
Me.OtherPB.Add(New PictureBox())
With Me.OtherPB(I)
.BackColor = Color.Transparent
.BorderStyle = BorderStyle.FixedSingle
.Width = 50
.Height = 50
End With
SetRandomPbLocation(Me.OtherPB(I))
Me.Controls.Add(Me.OtherPB(I))
Next
End Sub
Private Sub SetRandomPbLocation(ByVal pb As PictureBox)
'Just sets a random location for the picture boxes and ensures that it doesn't overlap with the center PB
Do While True
pb.Left = Rnd.Next(1, Me.Width - pb.Width)
pb.Top = Rnd.Next(1, Me.Height - pb.Height)
If (pb.Right < Me.MainPB.Left OrElse pb.Left > Me.MainPB.Right) AndAlso (pb.Top > Me.MainPB.Bottom OrElse pb.Bottom < Me.MainPB.Top) Then
Exit Do
End If
Loop
End Sub
Private Sub MainButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MainButton.Click
'Randomizes the location of the picture boxes
For Each PB In Me.OtherPB
SetRandomPbLocation(PB)
Next
'Will hold the closest control after the loop
Dim ClosestPB As Control = Nothing
Dim ClosestD, TempD As Double
For Each PB In Me.OtherPB
'Reset the control's background color
PB.BackColor = Color.Transparent
'Calculate the distance
TempD = GetDistanceBetweenToControls(PB, Me.MainPB)
If ClosestPB Is Nothing Then 'If this is the first time through the loop then just use this control as the closest
ClosestPB = PB
ClosestD = TempD
ElseIf TempD < ClosestD Then 'Otherwise if this control is closer than the current closest
ClosestPB = PB
ClosestD = TempD
End If
Next
'Set the closest controls background color so that we can see it
ClosestPB.BackColor = Color.Blue
End Sub
Private Shared Function GetDistanceBetweenToControls(ByVal controlToCheck As Control, ByVal baseControl As Control) As Double
If controlToCheck.Bottom < baseControl.Top Then
If controlToCheck.Right < baseControl.Left Then 'Above and to the left
Return DistanceBetweenTwoPoints(New Point(controlToCheck.Right, controlToCheck.Bottom), baseControl.Location)
ElseIf controlToCheck.Left > baseControl.Right Then 'above and to the right
Return DistanceBetweenTwoPoints(New Point(controlToCheck.Left, controlToCheck.Bottom), New Point(baseControl.Right, baseControl.Top))
Else 'Above
Return baseControl.Top - baseControl.Bottom
End If
ElseIf controlToCheck.Top > baseControl.Bottom Then
If controlToCheck.Right < baseControl.Left Then 'Below and to the left
Return DistanceBetweenTwoPoints(New Point(controlToCheck.Right, controlToCheck.Top), New Point(baseControl.Left, baseControl.Bottom))
ElseIf controlToCheck.Left > baseControl.Right Then 'Below and to the right
Return DistanceBetweenTwoPoints(controlToCheck.Location, New Point(baseControl.Right, baseControl.Bottom))
Else 'Below
Return controlToCheck.Top - baseControl.Bottom
End If
Else
If controlToCheck.Right < baseControl.Left Then 'Left
Return baseControl.Left - controlToCheck.Right
ElseIf controlToCheck.Left > baseControl.Right Then 'Right
Return controlToCheck.Left - baseControl.Right
End If
End If
End Function
Private Shared Function DistanceBetweenTwoPoints(ByVal point1 As Point, ByVal point2 As Point) As Double
'Standard distance formula
Return Math.Sqrt((Math.Abs(point2.X - point1.X) ^ 2) + (Math.Abs(point2.Y - point1.Y) ^ 2))
End Function
End Class

Related

Is there a way to add a clickable Icon to ComboBox?

I'm currently working on a WinForms App in vb.net, where you can select different data in a combobox. The items inside these combobox can be edited or deleted, so heres my question: Is there a way to add icons, e.g. a pencil and a trash, for each item to show the user "Click here to edit" or "Click here to delete"?
In my head it looks kind of the following picture:
Thank you very much :)
I created a new combo box control by inheriting a class from ComboBox.
Imports System.ComponentModel
Public Class ComboBoxEx
Inherits ComboBox
...
End Class
The idea is to use the DrawMode DrawMode.OwnerDrawFixed and to do all the drawing in code. This allows us to draw the images representing the clickable buttons. I added two images as resources to the project (My.Resources.pencil and My.Resources.Trash_16x16, yours might have different names).
Const IconSize = 20
Dim stringFormat As StringFormat = New StringFormat() With {.LineAlignment = StringAlignment.Center}
Public Sub New()
DrawMode = DrawMode.OwnerDrawFixed
DropDownStyle = ComboBoxStyle.DropDownList
ItemHeight = 21
End Sub
Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
e.DrawBackground()
If e.Index >= 0 Then
Dim g As Graphics = e.Graphics
Dim brushColor = If(((e.State And DrawItemState.Selected) <> 0),
SystemColors.Highlight,
e.BackColor)
Using brush As Brush = New SolidBrush(brushColor)
g.FillRectangle(brush, e.Bounds)
End Using
Using textBrush As Brush = New SolidBrush(e.ForeColor)
g.DrawString(Items(e.Index).ToString(), e.Font, textBrush, e.Bounds, stringFormat)
End Using
' Skip the default item at index = 0 and the text box area (DrawItemState.ComboBoxEdit)
If e.Index > 0 And (e.State And DrawItemState.ComboBoxEdit) = 0 Then
Dim image = My.Resources.pencil
Dim point = New Point(
Width - 2 * IconSize + (IconSize - image.Width) \ 2,
e.Bounds.Y + (ItemHeight - image.Height) \ 2)
g.DrawImage(image, point)
image = My.Resources.Trash_16x16
point = New Point(
Width - IconSize + (IconSize - image.Width) \ 2,
e.Bounds.Y + (ItemHeight - image.Height) \ 2)
g.DrawImage(image, point)
End If
End If
e.DrawFocusRectangle()
End Sub
This was the visual part. Now we must detect mouse clicks on the buttons in the drop down and also raise events when they are clicked.
Dim isDroppedDown As Boolean
Public Event Button1Clicked()
Public Event Button2Clicked()
Protected Overrides Sub OnDropDown(e As EventArgs)
isDroppedDown = True
MyBase.OnDropDown(e)
End Sub
Protected Overrides Sub OnDropDownClosed(e As EventArgs)
isDroppedDown = False
MyBase.OnDropDownClosed(e)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_COMMAND = &H111
If LicenseManager.UsageMode = LicenseUsageMode.Runtime And isDroppedDown And
m.Msg = WM_COMMAND And (CType(m.WParam, Int64) >> 16) = 1 Then
Dim button = ButtonClicked()
' If the user clicked a button (skipping default item)
If button <> 0 And SelectedIndex > 0 Then
m.Result = New IntPtr(1)
If button = 1 Then
RaiseEvent Button1Clicked()
Else
RaiseEvent Button2Clicked()
End If
Return
End If
End If
MyBase.WndProc(m)
End Sub
Private Function ButtonClicked() As Integer
Dim pos = PointToClient(MousePosition)
If pos.X > Size.Width - IconSize Then
Return 2
ElseIf pos.X > Size.Width - 2 * IconSize Then
Return 1
End If
Return 0
End Function
After compiling your project, this new ComboBoxEx appears in the winforms toolbox and you can drag and drop it to your form.
In the form you can then handle the button events
Private Sub ComboBoxEx1_Button1Clicked() Handles ComboBoxEx1.Button1Clicked
Label1.Text = $"Pen clicked. Item = {ComboBoxEx1.SelectedItem.ToString()}"
End Sub
Private Sub ComboBoxEx1_Button2Clicked() Handles ComboBoxEx1.Button2Clicked
Label1.Text = $"Trash bin clicked. Item = {ComboBoxEx1.SelectedItem.ToString()}"
End Sub
You may have to tweak the icon size, text size etc. to fit your needs.

Solved; See Edit5; Running a "For Each" Loop in Visual Basic "Space Invaders" Game seems to slowdown everything else

[Solved] See Edit5
I'm trying to make a "Space Invaders" game for a class and every time I turn on the timer handling the enemies the player movement slows down immensely. I'm almost sure the issue, or at least the bulk of it, comes from this loop:
For Each PicBox2 In EnemyList
If Not ((PicBox2.Left + EMovDir * ESpeed < 0) Or (PicBox2.Right + EMovDir * ESpeed >= Me.Width - 10)) Then
PicBox2.Location = New Point(PicBox2.Location.X + EMovDir * ESpeed, PicBox2.Location.Y)
If PicBox2.Bounds.IntersectsWith(Player.Bounds) Then GameOver = True
End If
Next
Here is a screenshot of the form design:
[enter image description here][1]
And here is the code I have so far:
Public Class Form1
Region Variables
Dim PSpeed As Integer = 5 'Pixeis de movimento do jogador por trigger do EventoKeyDown
Dim ESpeed As Integer = 10 'Pixeis de movimento dos inimigos por loop
Dim ENumber As Integer 'Número de inimigos
Dim PHealth As Integer 'Número de vezes que o jogador pode ser atingdo até GameOver
Dim PMoveStateL As Boolean
Dim PMoveStateR As Boolean
Dim GameOver As Boolean
Dim EMovDir As Integer = 1
Dim PicBox As PictureBox
Dim EnemyList As New List(Of PictureBox)
End Region
Region Game Load
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each PicBox In Me.Controls.OfType(Of PictureBox)
If PicBox.Tag = "Enemy" Then
EnemyList.Add(PicBox)
End If
Next
End Sub
End Region
Region InGame Ui
Private Sub StopStartTimer_Click(sender As Object, e As EventArgs) Handles Button3.Click
If EnemyTimer.Enabled = False Then
EnemyTimer.Enabled = True
Else EnemyTimer.Enabled = False
End If
End Sub
End Region
Region PlayerControls
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown 'Não Esquecer de ativar a Propriedade KeyPreview
If e.KeyCode = Keys.D And (Player.Location.X + PSpeed) < (Me.Width - Player.Width) Then
PMoveStateR = True
End If
If e.KeyCode = Keys.A And ((Player.Location.X - PSpeed) > 0) Then
PMoveStateL = True
End If
End Sub
Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
If e.KeyCode = Keys.D Then
PMoveStateR = False
End If
If e.KeyCode = Keys.A Then
PMoveStateL = False
End If
End Sub
Private Sub PlayerTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PlayerTimer.Tick
If PMoveStateR = True And (Player.Location.X + PSpeed) < (Me.Width - Player.Width) Then
Player.Location = New Point((Player.Location.X + PSpeed), Player.Location.Y)
End If
If PMoveStateL = True And ((Player.Location.X - PSpeed) > 0) Then
Player.Location = New Point((Player.Location.X - PSpeed), Player.Location.Y)
End If
End Sub
End Region
Region Enemy Behavior
Private Sub EnemyTimer_Tick(sender As Object, e As EventArgs) Handles EnemyTimer.Tick
For Each PicBox2 In EnemyList
If ((PicBox2.Left - ESpeed < 0) Or (PicBox2.Right + ESpeed >= Me.Width - 10)) Then
'Something I don't really understand seems to be going with the .Right reading where either it doesn't line up with the form width, or the form width doesn't line up with the form's zsize while running. The -10 pixels seem to offset it.
For Each PicBox3 As PictureBox In EnemyList
PicBox3.Location = New Point(PicBox3.Location.X, PicBox3.Location.Y + PicBox3.Height)
If PicBox.Bounds.IntersectsWith(Player.Bounds) Then GameOver = True
Next
End If
Next
For Each PicBox2 In EnemyList
If ((PicBox2.Left + EMovDir * ESpeed < 0) Or (PicBox2.Right + EMovDir * ESpeed >= Me.Width - 10)) Then
EMovDir = EMovDir * -1
End If
Next
For Each PicBox2 In EnemyList
If Not ((PicBox2.Left + EMovDir * ESpeed < 0) Or (PicBox2.Right + EMovDir * ESpeed >= Me.Width - 10)) Then
PicBox2.Location = New Point(PicBox2.Location.X + EMovDir * ESpeed, PicBox2.Location.Y)
If PicBox2.Bounds.IntersectsWith(Player.Bounds) Then GameOver = True
End If
Next
End Sub
End Region
End Class
The tutorial I was following used arrays instead of a list, but I thought I would try to code this in such a way that adding or removing enemies wouldn't require me to edit the code and learn how to along the way. I've already tried fixing everything that occurred to me could be the cause, which is how I've arrived at the code above. I really appreciate any help you can give in fixing this, as well as any advice, Thanks! (and sorry for my english)
Edit: I hadn't thought to make sure, but just confirmed the more "Enemy" picture boxes the worse the issue gets.
Edit2: Weird as it might sound I checked if maybe the slower player movement was an optical illusion due to the background and/or the aliens point of reference also being in movement. That was not it either.
Edit3: Running a third timer at a 1000 millisecond interval and displaying the number of seconds since the timer was enabled while checking against the stopwatch in my phone revealed that it's not that the entire form is slowing down. The issue is replicated regardless of any image size mode, or any other picturebox and timer property .
Edit4: If I stretch the form window while it's running so that the aliens aren't in view, the Player moves normally.
Edit5: [Solved] The issue only occurs if the form has a background image. I have absolutely no idea why .
It took me way too long to track down what was causing this bizarre issue, and it's bizarre properties.
Regardless, I would like to thank Idle_Mind and Hack_Slash for helping me improve my code and teaching me some cool new stuff.
The way you've got it written, EVERY time an enemy hits the left or right sides, all enemies will move down. But doesn't that mean they could all move down multiple times?...once for each time an enemy has hit the edge, which could be multiple enemies if they are in a column?
See if something more like this performs better:
Private Sub EnemyTimer_Tick(sender As Object, e As EventArgs) Handles EnemyTimer.Tick
Dim edgeWasHit = EnemyList.Any(Function(pb) (pb.Left - ESpeed < 0) OrElse (pb.Right + ESpeed >= Me.Width - 10))
Dim reverseDirection = EnemyList.Any(Function(pb) (pb.Left + EMovDir * ESpeed < 0) OrElse (pb.Right + EMovDir * ESpeed >= Me.Width - 10))
If reverseDirection Then
EMovDir = EMovDir * -1
End If
For Each PicBox2 In EnemyList
If edgeWasHit Then
' move it down
PicBox2.Location = New Point(PicBox2.Location.X, PicBox2.Location.Y + PicBox2.Height))
End If
' move left/right
PicBox2.Location = New Point(PicBox2.Location.X + EMovDir * ESpeed, PicBox2.Location.Y)
If PicBox2.Bounds.IntersectsWith(Player.Bounds) Then
GameOver = True
Exit Sub
End If
Next
End Sub

Restrain form to the working area of the screen without flickering

I have working code but when moving the form to the screen bounds I sometimes get form flickering. It doesn't look good.
Is there a better solution for this?
Private Sub Form1_Move(sender As Object, e As EventArgs) Handles Me.Move
Dim p As Point
p = Me.Location
Dim screenWidth As Integer = Screen.PrimaryScreen.Bounds.Width
Dim screenHeight As Integer = Screen.PrimaryScreen.Bounds.Height
Dim TaskBarH As Integer = screenHeight - Screen.PrimaryScreen.WorkingArea.Height
If p.X < 0 Then
Me.Location = New System.Drawing.Point(0, p.Y)
ElseIf p.X > screenWidth - Me.Size.Width Then
Me.Location = New System.Drawing.Point(screenWidth - Me.Size.Width, p.Y)
End If
If p.Y < 0 Then
Me.Location = New System.Drawing.Point(p.X, 0)
ElseIf p.Y > screenHeight - Me.Size.Height - TaskBarH Then
Me.Location = New System.Drawing.Point(p.X, screenHeight - Me.Size.Height - TaskBarH)
End If
End Sub
The reason your form is flickering when you are moving it around, is because every time the form is redrawn in a different location, the Move event is fired. To see just how much, try this:
Option Strict On
Option Explicit On
Public Class Form1
Dim counter As Int64
Private Sub Form1_Move(sender As Object, e As EventArgs) Handles Me.Move
counter += 1
Console.WriteLine(counter)
End Sub
End Class
You'll notice that anytime the form is moved even just one pixel, the event is getting fired. You move the form 100 pixels to the left, that event is going to fire 100 times. That's a lot of work to be checking the location on every redraw of the form and then computing if each individual point is within the bounds that you want. All that constant computing of the points and rectangles and such is causing a huge overhead and the result is not enough resource time to redraw all of the controls in the form, hence the flickering of the form.
The better option would be to do all of that math only once when the form is done moving and correct the form if it is outside of the screen bounds. There is a Form.ResizeEnd event that is fired anytime the user has finished re-sizing or moving a form. By utilizing this event, the code will only get called once after the user has finished their action and the form has redrawn itself on the screen. Then if the form is outside of the bounds that you want, you can move the form to a location that is.
Rather than hard-coding the use of Screen.PrimaryScreen, I am dynamically getting the screen in which the form resides. This allows the user to move the form to a different screen in a multi-monitor environment.
Option Strict On
Option Explicit On
Public Class Form1
Private Sub Form1_resizeEnd(sender As Object, e As EventArgs) Handles Me.ResizeEnd
Dim screenArea As Rectangle = Screen.GetWorkingArea(Me.Location)
Dim formArea As Rectangle = Me.DesktopBounds
If Me.WindowState = FormWindowState.Normal AndAlso Not screenArea.Contains(formArea) Then
If formArea.Top < screenArea.Top Then Me.Top = 0
If formArea.Left < screenArea.Left Then Me.Left = 0
If formArea.Right > screenArea.Right Then Me.Left = Me.Left - (formArea.Right - screenArea.Right)
If formArea.Bottom > screenArea.Bottom Then Me.Top = Me.Top - (formArea.Bottom - screenArea.Bottom)
End If
End Sub
End Class
You can try restricting where the mouse can drag the form like this.
Option Strict On
Option Explicit On
Option Infer Off
Imports System.Runtime.InteropServices
Public Class Form1
Public Declare Function SetCursorPos Lib "user32.dll" (X As Integer, Y As Integer) As Boolean
Public Function AbsoluteFormMousePosition() As Point
Dim mousePos As Point = PointToClient(MousePosition)
Dim hBorderWidth As Integer = (Me.Width - Me.ClientRectangle.Width) \ 2
Dim vBorderWidth As Integer = hBorderWidth
Dim TitleHeight As Integer = (Me.Height - Me.ClientRectangle.Height) - vBorderWidth
Dim newPos As New Point(mousePos.X + hBorderWidth, mousePos.Y + TitleHeight)
Return newPos
End Function
Function TryOffest(range As Range, find As Integer, ByRef newValue As Integer) As Boolean
If Not range.Contains(find) Then
Dim diff As Integer
diff = If(find > range.Upper, find - range.Upper, range.Lower - find)
newValue += If(find > range.Upper, -(diff), diff)
Return True
End If
Return False
End Function
Private Sub Form1_Move(sender As Object, e As EventArgs) Handles Me.Move
Dim mousePos As Point = MousePosition
Dim newX As Integer = mousePos.X
Dim newY As Integer = mousePos.Y
Dim vChanged, hChanged As Boolean
Dim hBorder As Integer = Me.Width - Me.ClientRectangle.Width
Dim vBorder As Integer = Me.Height - Me.ClientRectangle.Height
Dim adjX As Integer = Me.PointToClient(mousePos).X
Dim adjY As Integer = Me.PointToClient(mousePos).Y
Dim titleMousePosition As Integer = hBorder - Me.PointToClient(mousePos).Y
Dim verticalBounds As New Range(AbsoluteFormMousePosition.Y + 2, Screen.PrimaryScreen.WorkingArea.Height - (Me.Height - vBorder - adjY + 2))
Dim horizontalBounds As New Range(adjX + 2, Screen.PrimaryScreen.WorkingArea.Width - (Me.Width - hBorder - adjX + 2))
vChanged = TryOffest(verticalBounds, mousePos.Y, newY)
hChanged = TryOffest(horizontalBounds, mousePos.X, newX)
If vChanged OrElse hChanged Then SetCursorPos(newX, newY)
End Sub
End Class
Public Class Range
Public Lower, Upper As Integer
Sub New(lower As Integer, upper As Integer)
Me.Lower = lower : Me.Upper = upper
End Sub
Public Function Contains(number As Integer) As Boolean
If number >= Lower AndAlso number <= Upper Then Return True Else Return False
End Function
End Class

VB.NET Getting Value On Dynamic Control

I am new to VB and run into some problems.
I have created sub routine that will automatically add a control to a panelcontrol each time i click on the button, so it can create as many as i want.
Here is the code for the subroutine.
Private Sub CreateControl()
'CREATE TEXTBOX ITEMNO
Dim i_Itemno As Integer = TextEditItemno.Length
ReDim Preserve TextEditItemno(i_Itemno)
TextEditItemno(i_Itemno) = New TextEdit
With TextEditItemno(i_Itemno)
.Name = "Txtitemno" & i_Itemno.ToString()
If TextEditItemno.Length < 2 Then
.SetBounds(0, 0, 32, 20)
Else
.Left = TextEditItemno(i_Itemno - 1).Left
.Top = TextEditItemno(i_Itemno - 1).Top + TextEditItemno(i_Itemno - 1).Height + 4
.Size = TextEditItemno(i_Itemno - 1).Size
End If
.Tag = i_Itemno
End With
AddHandler TextEditItemno(i_Itemno).TextChanged, AddressOf TextEditItemno_TextChanged
PanelControl5.Controls.Add(TextEditItemno(i_Itemno))
'CREATE TEXTBOX PRICE
Dim i_Price As Integer = TextEditPrice.Length
ReDim Preserve TextEditPrice((i_Price))
Dim PriceX As Int16 = LblHarga.Location.X
TextEditPrice(i_Price) = New TextEdit
With TextEditPrice(i_Price)
.Name = "Txtprice" & i_Price.ToString()
If TextEditSatuan.Length < 2 Then
.SetBounds(PriceX, 0, 70, 20)
Else
.Left = TextEditPrice(i_Price - 1).Left
.Top = TextEditPrice(i_Price - 1).Top + TextEditPrice(i_Price - 1).Height + 4
.Size = TextEditPrice(i_Price - 1).Size
End If
.Tag = i_Price
End With
AddHandler TextEditPrice(i_Price).TextChanged, AddressOf TextEditPrice_TextChanged
PanelControl5.Controls.Add(TextEditPrice(i_Price))
End Sub
And i call it in a button click.
Private Sub btnNew_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNew.Click
CreateControl()
End Sub
Now what i am looking for is how to loop and get the value of those textboxes no matter how many textboxex i have create.
For i As Integer = 0 To TextEditItemno.Length - 1
' code to get the value of each textbox
Next
Thank you
This code goes in to your loop and gets the value of each textbox based on i.
Dim Text as String = TextEditItemno(i).Text
You may also be better served by using a List(of Textbox) rather than an array of textboxes. You don't need to worry about redimming the array, you can just do MyListOfTextboxes.Add(TheNewTextBox). You can still retrieve the value of each textbox the same way as the array.

VB.NET: Image as checkbox state box

Is it possible to use an image as the checkbox "checked" indicator square?
I know I can use a background image, but that goes behind the label aswell and also it is not possible (as far as I know) to align it.
How can I use an image instead of the square and leave the label and all other customization as they are?
Thanks in advance!
You look like this?
Dim frm As New Form
frm.Size = New Size(320, 200)
Dim iList As New ImageList
iList.Images.Add(Image.FromFile("check.png"), Color.White)
iList.Images.Add(Image.FromFile("uncheck.png"), Color.White)
Dim chk As New CheckBox
chk.Text = "Check Box With Image"
chk.AutoSize = False
chk.Size = New Size(350, 20)
chk.ImageList = iList
chk.ImageIndex = 1
chk.CheckAlign = ContentAlignment.MiddleRight
chk.ImageAlign = ContentAlignment.MiddleLeft
chk.TextImageRelation = TextImageRelation.ImageBeforeText
chk.Location = New Point(32, 32)
frm.Controls.Add(chk)
AddHandler chk.CheckStateChanged,
Sub(sender1 As Object, e1 As EventArgs)
chk.ImageIndex = IIf(chk.Checked, 0, 1)
End Sub
frm.ShowDialog()
UPDATE #1: Actually, #brahm solution's below is much better than mine!
UPDATE #2: Actually, it's not. Now I see how he did it: he's moving the checkbox out of sight by placing it way off the visible Form's area. Not a great solution...
The ideal solution would be to subclass the CheckBox control and do your own rendering by overriding the OnPaint method.
An easier, although probably messier solution, would be to place a PictureBox over the check box and control the image in the PictureBox through the CheckBox's CheckedChange event.
Another option:
You could still use the CheckBox in button mode (Appearance = Button), as you suggested, but then add a label right next to it.
Then, handle the Click event on the Label to toggle the Checked state of the CheckBox. Then end result should provide you exactly what you are looking for.
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.VisualStyles
Public Class ImageCheckBox
Public State As CheckBoxState = CheckBoxState.UncheckedNormal
Public Hot As Boolean = False
Public Pressed As Boolean = False
Public ImageDictionary As Dictionary(Of CheckBoxState, Image) = New Dictionary(Of CheckBoxState, Image)
Private Const PaddingModifier As Integer = 2
Sub New()
Me.New(New Dictionary(Of CheckBoxState, Image) From {
{CheckBoxState.CheckedDisabled, My.Resources.form_checkbox_checked},
{CheckBoxState.CheckedHot, My.Resources.form_checkbox_checked},
{CheckBoxState.CheckedNormal, My.Resources.form_checkbox_checked},
{CheckBoxState.CheckedPressed, My.Resources.form_checkbox_checked},
{CheckBoxState.UncheckedDisabled, My.Resources.form_checkbox_unchecked},
{CheckBoxState.UncheckedHot, My.Resources.form_checkbox_unchecked},
{CheckBoxState.UncheckedNormal, My.Resources.form_checkbox_unchecked},
{CheckBoxState.UncheckedPressed, My.Resources.form_checkbox_unchecked}})
End Sub
Sub New(imageDictionary As Dictionary(Of CheckBoxState, Image))
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.ImageDictionary = imageDictionary
End Sub
Sub CheckBox_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
'Return if the specific Image is not found
If Not ImageDictionary.ContainsKey(State) Then Return
'Get the Size of the CheckBox
Dim glyphSize As Size = CheckBoxRenderer.GetGlyphSize(e.Graphics, State)
'Get the Location of the CheckBox in relation to the Alignment of it
Dim glyphLocation As Point
Select Case Me.CheckAlign
Case Drawing.ContentAlignment.TopLeft
glyphLocation = New Point(Me.Padding.Left, Me.Padding.Top)
Exit Select
Case Drawing.ContentAlignment.TopCenter
glyphLocation = New Point(Me.Padding.Left + (Me.Width - glyphSize.Width) / 2, Me.Padding.Top)
Exit Select
Case Drawing.ContentAlignment.TopRight
glyphLocation = New Point(Me.Padding.Left + Me.Width - glyphSize.Width, Me.Padding.Top)
Exit Select
Case Drawing.ContentAlignment.MiddleLeft
glyphLocation = New Point(Me.Padding.Left, Me.Padding.Top + (Me.Height - glyphSize.Height) / 2)
Exit Select
Case Drawing.ContentAlignment.MiddleCenter
glyphLocation = New Point(Me.Padding.Left + (Me.Width - glyphSize.Width) / 2, Me.Padding.Top + (Me.Height - glyphSize.Height) / 2)
Exit Select
Case Drawing.ContentAlignment.MiddleRight
glyphLocation = New Point(Me.Padding.Left + Me.Width - glyphSize.Width, Me.Padding.Top + (Me.Height - glyphSize.Height) / 2)
Exit Select
Case Drawing.ContentAlignment.BottomLeft
glyphLocation = New Point(Me.Padding.Left, Me.Padding.Top + Me.Height - glyphSize.Height)
Exit Select
Case Drawing.ContentAlignment.BottomCenter
glyphLocation = New Point(Me.Padding.Left + (Me.Width - glyphSize.Width) / 2, Me.Padding.Top + Me.Height - glyphSize.Height)
Exit Select
Case Drawing.ContentAlignment.BottomRight
glyphLocation = New Point(Me.Padding.Left + Me.Width - glyphSize.Width, Me.Padding.Top + Me.Height - glyphSize.Height)
Exit Select
End Select
'Set the drawing Area
Dim glyphRectangle As Rectangle = New Rectangle(glyphLocation, glyphSize)
'Enlarge the Rectangle to completely hide default symbol
Dim clearRectangle As Rectangle = New Rectangle(glyphLocation.X - PaddingModifier,
glyphLocation.Y - PaddingModifier,
glyphSize.Width + 2 * PaddingModifier,
glyphSize.Height + 2 * PaddingModifier)
'Draw the Parent Background over the default CheckBox to clear it
CheckBoxRenderer.DrawParentBackground(e.Graphics, clearRectangle, Me)
Debug.WriteLine(State)
'Finally draw the custom CheckBox image on the position of the default one
e.Graphics.DrawImage(ImageDictionary(State), glyphRectangle)
End Sub
Sub CheckBox_MouseClick(sender As Object, e As EventArgs) Handles Me.MouseClick
Me.Checked = Not Me.Checked
End Sub
Sub CheckBox_MouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
Me.Pressed = True
End Sub
Sub CheckBox_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
Me.Pressed = False
End Sub
Sub CheckBox_MouseEnter(sender As Object, e As EventArgs) Handles Me.MouseEnter
Me.Hot = True
End Sub
Sub CheckBox_MouseLeave(sender As Object, e As EventArgs) Handles Me.MouseLeave
Me.Hot = False
End Sub
Public Sub updateState() Handles Me.MouseClick, Me.MouseDown, Me.MouseUp, Me.MouseEnter, Me.MouseLeave, Me.EnabledChanged
Debug.WriteLine(Me.Checked & " " & Me.Enabled & " " & Me.Hot & " " & Me.Pressed)
Me.State = CurrentState()
Me.Refresh()
Debug.WriteLine(State)
End Sub
Public Function CurrentState() As CheckBoxState
If (Me.Checked) Then
If (Not Me.Enabled) Then Return CheckBoxState.CheckedDisabled
If (Me.Pressed) Then Return CheckBoxState.CheckedPressed
If (Me.Hot) Then Return CheckBoxState.CheckedHot
Return CheckBoxState.CheckedNormal
Else
If (Not Me.Enabled) Then Return CheckBoxState.UncheckedDisabled
If (Me.Pressed) Then Return CheckBoxState.UncheckedPressed
If (Me.Hot) Then Return CheckBoxState.UncheckedHot
Return CheckBoxState.UncheckedNormal
End If
End Function
End Class
I also had this problem with a mute/unmute audiotrack i first went for the CheckBox but deceided to just use the PictureBox click event and used .location to get the New Point overlay the other PictureBox and enable the visibility of the one or the other box, works fine for a complete newb that i am :-)
Picture of the PictureBox in the Designer
Private Sub PictureBoxMute_Click(sender As Object, e As EventArgs) Handles PictureBoxMute.Click
PictureBoxMute.Visible = False
PictureBoxUnmute.Location = New Point(590, 433)
PictureBoxUnmute.Visible = True
Volume = 0
myplayer.Volume = Volume
End Sub
Private Sub PictureBoxUnmute_Click(sender As Object, e As EventArgs) Handles PictureBoxUnmute.Click
PictureBoxUnmute.Visible = False
PictureBoxMute.Visible = True
Volume = 1
myplayer.Volume = Volume
End Sub