I'm pretty new to programming. I'm trying to code OOP as much as possible .
Problem: How can I detected if two graphics make a collision with each other in a single picturebox?
Now here are the questions:
Is there anyway I can do this with graphics on a bitmap. I have seen a lot of examples where two separate picterbox are used to make a collision with each, but how to do this with my example?
How can I improve my current code?
Public Class RechtHoek
'property
Public Property Y As Integer
Set(value As Integer)
_y = value
End Set
Get
Return _y
End Get
End Property
Public Property X As Integer
Set(value As Integer)
_x = value
End Set
Get
Return _x
End Get
End Property
'Variables
Dim _y As Integer
Dim _x As Integer
Dim _maxY As Integer
Dim _maxX As Integer
'Variabele om kleur te wijzingen
Dim kleur As Color = RandomKleur()
Dim VeranderKleur As Boolean = False
Dim brush2 As New SolidBrush(RandomKleur)
'variablen om rechthoek te draaien
'Variabelen voor bewegen van de rechthoeken
Dim goingUp As Boolean = False
Dim goingRight As Boolean = False
'constructor
Public Sub New(x As Integer, y As Integer, maxX As Integer, MaxY As Integer)
_y = y
_x = x
_maxX = maxX
_maxY = MaxY
End Sub
Public Sub DrawRechthoekHorizontaal(graph As Graphics)
Dim pen As New Pen(Color.Black)
Dim rect As New Rectangle(_x, Y, 40, 100)
graph.FillRectangle(brush2, rect)
graph.DrawRectangle(pen, rect)
End Sub
Public Sub DrawRechthoekVerticaal(graph As Graphics)
Dim pen As New Pen(Color.Black)
Dim rect As New Rectangle(_x, Y, 100, 40)
graph.FillRectangle(brush2, rect)
graph.DrawRectangle(pen, rect)
End Sub
Public Function RandomKleur() As Color
Dim myAlpha As Integer = 0
Dim myRed As Integer = 0
Dim myGreen As Integer = 0
Dim myBlue As Integer = 0
Randomize()
myAlpha = CInt(Int((254 * Rnd()) + 0))
Randomize()
myGreen = CInt(Int((254 * Rnd()) + 0))
Randomize()
myRed = CInt(Int((254 * Rnd()) + 0))
Randomize()
myBlue = CInt(Int((254 * Rnd()) + 0))
'Color.FromArgb(myAlpha, myRed, myGreen, myBlue)
Return Color.FromArgb(myAlpha, myRed, myGreen, myBlue)
End Function
Public Sub Up()
Y -= 10
End Sub
Public Sub Down()
Y += 10
End Sub
Public Sub Right()
X -= 10
End Sub
Public Sub Left()
X += 10
End Sub
Public Sub MoveX()
If goingRight Then
Right()
Else
Left()
End If
If X < 0 Then
goingRight = False
'verander van kleur
If X = -10 Then
VeranderKleur = True
brush2.Color = RandomKleur()
End If
ElseIf X > 1600 Then
goingRight = True
'verander van kleur
If X = 1610 Then
VeranderKleur = False
brush2.Color = RandomKleur()
End If
End If
End Sub
Public Sub MoveY()
If goingUp Then
Up()
Else
Down()
End If
If Y < 0 Then
goingUp = False
'verander van kleur
If Y = -10 Then
VeranderKleur = True
brush2.Color = RandomKleur()
End If
ElseIf Y > 900 Then
goingUp = True
'verander van kleur
If Y = 910 Then
VeranderKleur = True
brush2.Color = RandomKleur()
End If
End If
End Sub
End Class
Public Class FormBewegendeRechtHoeken
Dim rectY As RechtHoek
Dim rectX As RechtHoek
Dim bitmap As Bitmap
Private img As Image
Private Sub FormRechtHoek_Load(sender As Object, e As EventArgs) Handles MyBase.Load
bitmap = New Bitmap(PictureBox.Width, PictureBox.Height)
'X = plaats waar rechthoek start op x as , Y = plaats waar rechthoekstart op y as 'Max x hoogete &' Max Y hoogte
rectY = New RechtHoek(PictureBox.Width \ 2, PictureBox.Height, PictureBox.Width, PictureBox.Height)
rectX = New RechtHoek(PictureBox.Width, PictureBox.Height \ 2, PictureBox.Width, PictureBox.Height)
Draw()
TheTimer.Start()
End Sub
Private Sub Botsing()
End Sub
Private Sub Draw()
Using graph As Graphics = Graphics.FromImage(bitmap)
graph.Clear(Color.Yellow)
rectX.DrawRechthoekHorizontaal(graph)
'Omkeren van de rechthoek in het midden
If rectY.Y < 450 Then
rectY.DrawRechthoekHorizontaal(graph)
End If
If rectY.Y > 450 Then
rectY.DrawRechthoekVerticaal(graph)
End If
End Using
PictureBox.Image = bitmap
End Sub
Private Sub TheTimer_Tick(sender As Object, e As EventArgs) Handles TheTimer.Tick
rectY.MoveY()
rectX.MoveX()
Draw()
End Sub
End Class
If you save the rectangle structures you draw in an array or a list, you can check a collision like this:
The rectangles collide when one rectangle contains at least one edge point of the other rectangle. So you can define a function:
Function CheckRectCollision(Rect1 as Rectangle, Rect2 as Rectangle) as Boolean
If Rect1.Contains(New Point(Rect2.Left, Rect2.Top) Orelse _
Rect1.Contains(New Point(Rect2.Left, Rect2.Bottom) Orelse _
Rect1.Contains(New Point(Rect2.Right, Rect2.Top) Orelse _
Rect1.Contains(New Point(Rect2.Right, Rect2.Bottom) Then
Return True
Else
Return False
End If
End Function
That way you can check if two rectangles collide. Find exclusive permutations for all rectangle pairs you have and call the function for each pair.
Related
I'm a high-school student making a basic game for an assessment.
The game makes baseballs fly across the screen at the player, who bats them away.
All baseballs are contained in pictureboxes, and I need to be able to make an infinite number of them which can be individually referenced and moved, all during runtime.
I'm currently adding and storing pictureboxes in dictionaries. However, whenever I create a new picturebox and add it to the form, it overrides any previously created pictureboxes on the form.
I need to find code that allows previously created pictureboxes to remain on the form while creating new ones.
For context, I have added the logic flow of my program below.
Current Program Logic
Sub that randomly determines whether the ball will spawn on the left, right, north or south sides of the screen.
Sub that based on the above result, sets up a new "baseball" picturebox by altering a blank variable.
Sub that adds this as the Value to a dictionary, with the Key a variable called ballNameNumber.
At the same time, a random number is set as the Value for a dictionary called ballVelocity, with Key ballNameNumber. This is multiplied by a variable called level, which increases as the game time increases.
So at this point, a ball has been created, with identical key names in 3 dictionaries that each store its picturebox values and speed.
Sub that randomly chooses 1 of 4 spawn locations, records the direction of the ball to a dictionary called ballDirection based on this value, then creates the ball at this location.
All these subs occur in this order per tick of a timer called tmrGameTime (interval 500), on a form called frmGame (size 700,700)
'Dictionaries used to log and describe the movement of the balls onscreen.
Dim spawnedBalls As Dictionary(Of Integer, PictureBox) = New Dictionary(Of Integer, PictureBox)
Dim ballVelocity As Dictionary(Of Integer, Integer) = New Dictionary(Of Integer, Integer)
Dim ballDirection As Dictionary(Of Integer, String) = New Dictionary(Of Integer, String)
'Variables used in the composition of dictionaries.
Dim ballNameNumber As Integer = 1
Dim numberOfBalls As Integer = 0
Dim level As Integer = 1
Dim ball As New Picturebox
'Turns on the game timer
Private Sub frmGame_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
tmrGameTime.Enabled = True
tmrGameTime.Start()
End Sub
'Sets values to the ball variable (must be done within a sub otherwise an error is dispayed)
Public Sub ballSetUpLeft(ByRef ballTemplate)
ballTemplate.SizeMode = PictureBoxSizeMode.StretchImage
ballTemplate.Width = 34
ballTemplate.Height = 29
ballTemplate.Top = 325
ballTemplate.Left = 55
ballTemplate.Image = My.Resources.Baseball_Sprite
End Sub
Public Sub ballSetUpRight(ByRef ballTemplate)
ballTemplate.SizeMode = PictureBoxSizeMode.StretchImage
ballTemplate.Width = 34
ballTemplate.Height = 29
ballTemplate.Top = 325
ballTemplate.Left = 593
ballTemplate.Image = My.Resources.Baseball_Sprite
End Sub
Public Sub ballSetUpTop(ByRef ballTemplate)
ballTemplate.SizeMode = PictureBoxSizeMode.StretchImage
ballTemplate.Width = 34
ballTemplate.Height = 29
ballTemplate.Top = 59
ballTemplate.Left = 333
ballTemplate.Image = My.Resources.Baseball_Sprite
End Sub
Public Sub ballSetUpBottom(ByRef ballTemplate)
ballTemplate.SizeMode = PictureBoxSizeMode.StretchImage
ballTemplate.Width = 34
ballTemplate.Height = 29
ballTemplate.Top = 574
ballTemplate.Left = 333
ballTemplate.Image = My.Resources.Baseball_Sprite
End Sub
'Generates a random speed for a spawned ball based on the level value.
Public Function generateBallSpeed(ByVal level) As Integer
Randomize()
Dim ans As Integer = (((Rnd() * 10) * level) + 1)
Return ans
End Function
'Logs the ball data into dictionaries
Public Sub createBall(ByRef spawnedBalls, ByRef ballVelocity, ByRef ballNameNumber, ByRef numberOfBalls, ByRef ballTemplate)
'Adds a new ball with name and ballTemplate values to the dictionary.
spawnedBalls.Add(ballNameNumber, ballTemplate)
Dim v As Integer = generateBallSpeed(level)
'Using the matching name, adds a velocity value to the ball
ballVelocity.Add(ballNameNumber, v)
End Sub
'Spawns ball at a specific location on the form.
Public Sub spawnBallAtPitcher()
Randomize()
Dim pitcher As Integer = Int((4 - 1 + 1) * Rnd() + 1)
Select Case pitcher
Case 1
Call ballSetUpLeft(ballTemplate)
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, ballTemplate)
ballDirection.Add(ballNameNumber, "Left")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
Case 2
Call ballSetUpRight(ballTemplate)
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, ballTemplate)
ballDirection.Add(ballNameNumber, "Right")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
Case 3
Call ballSetUpTop(ballTemplate)
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, ballTemplate)
ballDirection.Add(ballNameNumber, "Top")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
Case 4
Call ballSetUpBottom(ballTemplate)
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, ballTemplate)
ballDirection.Add(ballNameNumber, "Down")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
End Select
End Sub
'Repeatedly spawns balls
Private Sub tmrGameTime_Tick_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrGameTime.Tick
Call spawnBallAtPitcher()
End Sub
End Class
I expect that the pictureboxes will appear in random order on my form, until four a visible at different points (as any further pictureboxes will be created on top of the previous). However, on picturebox is created and then jumps between spawn locations. No error messages are received.
Your problem is you're using only 1 reference of ball in your code. So each time you want to create another ball, you use replace the previous ball with your new ball. Change your code in the 4 procedures into functions then return the new ball.
'Sets values to the ball variable (must be done within a sub otherwise an error is dispayed)
Public Function ballSetUpLeft() As PictureBox
Dim newBall As New PictureBox
newBall.SizeMode = PictureBoxSizeMode.StretchImage
newBall.Width = 34
newBall.Height = 29
newBall.Top = 325
newBall.Left = 55
newBall.Image = My.Resources.Baseball_Sprite
Return newBall
End Function
Public Function ballSetUpRight() As PictureBox
Dim newBall As New PictureBox
newBall.SizeMode = PictureBoxSizeMode.StretchImage
newBall.Width = 34
newBall.Height = 29
newBall.Top = 325
newBall.Left = 593
newBall.Image = My.Resources.Baseball_Sprite
Return newBall
End Function
Public Function ballSetUpTop() As PictureBox
Dim newBall As New PictureBox
newBall.SizeMode = PictureBoxSizeMode.StretchImage
newBall.Width = 34
newBall.Height = 29
newBall.Top = 59
newBall.Left = 333
newBall.Image = My.Resources.Baseball_Sprite
Return newBall
End Function
Public Function ballSetUpBottom() As PictureBox
Dim newBall As New PictureBox
newBall.SizeMode = PictureBoxSizeMode.StretchImage
newBall.Width = 34
newBall.Height = 29
newBall.Top = 574
newBall.Left = 333
newBall.Image = My.Resources.Baseball_Sprite
Return newBall
End Function
Then change how you call the functions (used to be procedures) like this:
Select Case pitcher
Case 1
Dim newBall = ballSetUpLeft()
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, newBall)
ballDirection.Add(ballNameNumber, "Left")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
Case 2
Dim newBall = ballSetUpRight()
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, newBall)
ballDirection.Add(ballNameNumber, "Right")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
Case 3
Dim newBall = ballSetUpTop()
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, newBall)
ballDirection.Add(ballNameNumber, "Top")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
Case 4
Dim newBall = ballSetUpBottom()
Call createBall(spawnedBalls, ballVelocity, ballNameNumber, numberOfBalls, newBall)
ballDirection.Add(ballNameNumber, "Down")
ballNameNumber += 1
Me.Controls.Add(spawnedBalls.Item(ballNameNumber - 1))
End Select
Now your balls won't be replaced each time you want to create another ball. I leave the task to animate the balls and removing the balls when the pitcher hit the balls.
I tried to reduce redundancy in your code. The 3 dictionaries are replaced by a single dictionary which hold the ball information (id, direction, velocity and the picture). Use enumeration instead of hard-coded numbers for direction. Use Random class instead of Randomize() and Rnd() functions.
Public Class frmGame
'Dictionaries used to log and describe the movement of the balls onscreen.
Private balls = New Dictionary(Of Integer, Ball)
'Variables used in the composition of dictionaries.
Private random As New Random
Private lastBallID As Integer
Private Const level As Integer = 1
Private Sub frmGame_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Turns on the game timer
tmrGameTime.Enabled = True
tmrGameTime.Start()
End Sub
Private Sub tmrGameTime_Tick_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrGameTime.Tick
'Repeatedly spawns balls
spawnBallAtPitcher()
End Sub
'Spawns ball at a specific location on the form.
Private Sub spawnBallAtPitcher()
lastBallID += 1
Dim pitcher As Direction = random.Next(4) + 1
Dim newBall = createBall(lastBallID, pitcher, random)
Controls.Add(newBall.Picture)
'Logs the ball data into dictionaries
balls.Add(lastBallID, newBall)
End Sub
'Create new ball, set its ID, direction, velocity and picture
Private Function createBall(ballID As Integer, direction As Direction, rnd As Random) As Ball
Return New Ball With {
.BallID = ballID,
.Direction = direction,
.Velocity = generateBallSpeed(level, rnd),
.Picture = generateBallPicture(direction)
}
End Function
'Generates a random speed for a spawned ball based on the level value.
Private Function generateBallSpeed(ByVal level As Integer, ByVal rnd As Random) As Integer
Return (rnd.Next(10) + 1) * level
End Function
'Generates a new picture of ball
Private Function generateBallPicture(direction As Direction) As PictureBox
Dim location = generatePictureLocation(direction)
Return New PictureBox With {
.SizeMode = PictureBoxSizeMode.StretchImage,
.Width = 34,
.Height = 29,
.Top = location.Y,
.Left = location.X,
.Image = My.Resources.Baseball_Sprite
}
End Function
'Generates a location for new picture of ball
Private Function generatePictureLocation(direction As Direction) As Point
Select Case direction
Case Direction.Left
Return New Point(55, 325)
Case Direction.Right
Return New Point(593, 325)
Case Direction.Top
Return New Point(333, 59)
Case Direction.Bottom
Return New Point(333, 574)
End Select
End Function
End Class
Public Enum Direction
Left = 1
Right
Top
Bottom
End Enum
Public Class Ball
Public Property BallID As Integer
Public Property Velocity As Integer
Public Property Direction As Integer
Public Property Picture As PictureBox
End Class
I'm trying to get data from a DGV grid onto specific tags, and so far it has been working great. But an update moved the tags positions in the DGV so Rows(x) does not equal the tags I'm moving data into anymore.
Is it possible to do a search like the one I'm doing in Cells("Val") but in the Rows("") instead?
Actually I want it to be something like this Rows("TagIndex = 5") etc.
A full line of code would then be:
HopperStatus = dgvDataFlt.Rows("TagIndex = 5").Cells("Val").Value
but is this possible.
Row 12 & 13 are switched when logging
dgvDataFLT = dgvDataFloating
If dgvDataFlt.Rows(0).Cells("TagIndex").Value = 12 Then
'DGVDataFlt.AutoResizeColumns()
'--------------------------------------Floating TAGS fra database------------------------------------------
ProdRecCnt = dgvDataFlt.Rows(10).Cells("Val").Value
ProdTotCnt = dgvDataFlt.Rows(9).Cells("Val").Value
FrontFree = dgvDataFlt.Rows(8).Cells("Val").Value
CurrAutoMode = dgvDataFlt.Rows(7).Cells("Val").Value
PalletStatus = dgvDataFlt.Rows(6).Cells("Val").Value
HopperStatus = dgvDataFlt.Rows(5).Cells("Val").Value
PowerStatus = dgvDataFlt.Rows(4).Cells("Val").Value
CurrRecNo = dgvDataFlt.Rows(3).Cells("Val").Value
NomCycTime = dgvDataFlt.Rows(2).Cells("Val").Value
AutoStart = dgvDataFlt.Rows(1).Cells("Val").Value
MachineNo = dgvDataFlt.Rows(0).Cells("Val").Value
LOGTimeStamp = dgvDataFlt.Rows(0).Cells("DateAndTime").Value 'for aktuelle lognings tidstempel
LOGDateStamp = Microsoft.VisualBasic.Left(LOGTimeStamp, 10)
LOGClockStamp = Microsoft.VisualBasic.Mid(LOGTimeStamp, 12, 5)
End If
I want the code to look/work something like this:
If dgvDataFlt.Rows(0).Cells("TagIndex").Value = 12 Then
'DGVDataFlt.AutoResizeColumns()
'--------------------------------------Floating TAGS fra database------------------------------------------
ProdRecCnt = dgvDataFlt.Rows("TagIndex = 10").Cells("Val").Value
ProdTotCnt = dgvDataFlt.Rows("TagIndex = 9").Cells("Val").Value
FrontFree = dgvDataFlt.Rows("TagIndex = 8").Cells("Val").Value
CurrAutoMode = dgvDataFlt.Rows("TagIndex = 7").Cells("Val").Value
PalletStatus = dgvDataFlt.Rows("TagIndex = 6").Cells("Val").Value
HopperStatus = dgvDataFlt.Rows("TagIndex = 5").Cells("Val").Value
PowerStatus = dgvDataFlt.Rows("TagIndex = 4").Cells("Val").Value
CurrRecNo = dgvDataFlt.Rows("TagIndex = 3").Cells("Val").Value
NomCycTime = dgvDataFlt.Rows("TagIndex = 2").Cells("Val").Value
AutoStart = dgvDataFlt.Rows("TagIndex = 1").Cells("Val").Value
MachineNo = dgvDataFlt.Rows("TagIndex = 0").Cells("Val").Value
LOGTimeStamp = dgvDataFlt.Rows(0).Cells("DateAndTime").Value 'for aktuelle lognings tidstempel
LOGDateStamp = Microsoft.VisualBasic.Left(LOGTimeStamp, 10)
LOGClockStamp = Microsoft.VisualBasic.Mid(LOGTimeStamp, 12, 5)
End If
I would suggest adding a class and then inheriting the DataGridView control into that class. I have made a quick little example of this and the code works, but to get it to work you will have to perform a few steps:
(1) If you don't already have a windows forms application to test this then,
make a new one.
(2) Create class named KeyedDataGridView
(3) Copy and Paste the following Code into KeyedDataGridView class
(4) Rebuild your Project
(5) Drag and Drop new component onto your windows Form.
NOTE: This class is limited, but should still be able to do what you require of it.
Finally, if you need any help then, please leave a comment and will try to get to it when I can.
Option Explicit On
Public Class KeyedDataGridView
Inherits Windows.Forms.DataGridView
Dim _Rows As KeyedDataRows
Public Shadows Property Rows As KeyedDataRows
Get
Return _Rows
End Get
Set(value As KeyedDataRows)
_Rows = value
End Set
End Property
Public Sub New()
Dim strName As String
strName = Me.Name
strName = MyBase.Name
_Rows = New KeyedDataRows(Me)
_Rows.Rows = MyBase.Rows
End Sub
Protected Overrides Sub Finalize()
_Rows = Nothing
MyBase.Finalize()
End Sub
End Class
Public Class KeyedDataRows
Inherits Windows.Forms.DataGridViewRowCollection
Dim _TagNames As Dictionary(Of String, Integer)
Dim _Rows As DataGridViewRowCollection
Dim _Cells As Dictionary(Of String, DataGridViewCellCollection)
Dim dgv As DataGridView
Default Public Overloads ReadOnly Property Item(strTagName As String) As DataGridViewRow
Get
Return _Rows.Item(Me.IndexFromName(strTagName))
End Get
End Property
Protected Friend Property Rows As DataGridViewRowCollection
Get
Return _Rows
End Get
Set(value As DataGridViewRowCollection)
_Rows = value
End Set
End Property
Public Property TagName(index As Integer) As String
Get
Return CStr(_TagNames.Item(index))
End Get
Set(value As String)
_TagNames.Item(index) = value
End Set
End Property
Public Sub New(tmp As DataGridView)
MyBase.New(tmp)
dgv = tmp
_TagNames = New Dictionary(Of String, Integer)
_Cells = New Dictionary(Of String, DataGridViewCellCollection)
End Sub
Public Shadows Sub Add(strTagName As String)
Dim intCurRow As Integer
If dgv.AllowUserToAddRows Then
intCurRow = _Rows.Count - 1
Else
intCurRow = _Rows.Count
End If
_TagNames.Add(strTagName, intCurRow)
_Rows.Add()
End Sub
Public Shadows Sub Add(strTagName As String, dataGridViewRow As DataGridViewRow)
Dim intCurRow As Integer
If dgv.AllowUserToAddRows Then
intCurRow = _Rows.Count - 1
Else
intCurRow = _Rows.Count
End If
_TagNames.Add(strTagName, intCurRow)
_Rows.Add(dataGridViewRow)
End Sub
Public Shadows Sub Add(count As Integer, strTagNames() As String)
Dim intI As Integer
Dim intCurRow As Integer
If dgv.AllowUserToAddRows Then
intCurRow = _Rows.Count - 1
Else
intCurRow = _Rows.Count
End If
For intI = 0 To (count - 1)
_TagNames.Add(strTagNames(intI), intCurRow)
_Rows.Add()
intCurRow = _Rows.Count - 1
Next intI
End Sub
Public Property IndexFromName(strTagName As String) As Integer
Get
If _TagNames.Count > 0 Then
If _TagNames.ContainsKey(strTagName) Then
Return _TagNames.Item(strTagName)
Else
Return -1
End If
Else
Return -1
End If
End Get
Set(value As Integer)
_TagNames.Add(strTagName, value)
End Set
End Property
Public Overloads Sub RemoveAt(strTagName As String)
_Cells.Remove(strTagName)
_Rows.RemoveAt(IndexFromName(strTagName))
_TagNames.Remove(strTagName)
End Sub
Protected Overrides Sub Finalize()
_TagNames.Clear()
_TagNames = Nothing
_Cells.Clear()
_Rows.Clear()
_Cells = Nothing
_Rows = Nothing
MyBase.Finalize()
End Sub
End Class
I also, added the following buttons to a windows form to test the code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
With KeyedDataGridView1
.Rows.Add("Tag Test 1")
.Rows.Add("Tag Test 2")
.Rows.Add("Tag Test 3")
.Rows.Add("Tag Test 4")
End With
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
MsgBox(KeyedDataGridView1.Rows("Tag Test 3").Cells(0).Value & vbCrLf &
KeyedDataGridView1.Rows("Tag Test 3").Cells(1).Value & vbCrLf &
KeyedDataGridView1.Rows("Tag Test 3").Cells(2).Value)
MsgBox(KeyedDataGridView1.Rows("Tag Test 2").Cells(0).Value & vbCrLf &
KeyedDataGridView1.Rows("Tag Test 2").Cells(1).Value & vbCrLf &
KeyedDataGridView1.Rows("Tag Test 2").Cells(2).Value)
MsgBox(KeyedDataGridView1.Rows("Tag Test 1").Cells(0).Value & vbCrLf &
KeyedDataGridView1.Rows("Tag Test 1").Cells(1).Value & vbCrLf &
KeyedDataGridView1.Rows("Tag Test 1").Cells(2).Value)
End Sub
I want to color every same word inside a RichTextBox. I can do it for one line but not on multiple lines.
E.g., Welcome "user" .....
I want the word user to be an exact color in every line it's found.
Here's with what i came up so far:
RichTextBox1.Text = "Welcome "
RichTextBox1.Select(RichTextBox1.TextLength, 0)
RichTextBox1.SelectionColor = My.Settings.color
RichTextBox1.AppendText(My.Settings.username)
RichTextBox1.SelectionColor = Color.Black
RichTextBox1.AppendText(" ........." + vbCrLf)
It's on form.Load; I tried to use the richtextbox.TextChange event, but it just colors the last user word and the others are remain the same.
This is a simple Class that enables multiple Selections and Highlights of text for RichTextBox and TextBox controls.
You can use multiple instances of this Class for different controls.
You can add the Words to Select/HighLight to a List and specify which color to use for selecting and/or highlighting the text.
Dim listOfWords As WordList = New WordList(RichTextBox1)
listOfWords.AddRange({"Word1", "Word2"})
listOfWords.SelectionColor = Color.LightBlue
listOfWords.HighLightColor = Color.Yellow
These are the visual results of the Class actions:
In the example, the List of words is filled using:
Dim patterns As String() = TextBox1.Text.Split()
listOfWords.AddRange(patterns)
In the visual example, the Class is configured this way:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim patterns As String() = TextBox1.Text.Split()
Dim listOfWords As WordList = New WordList(RichTextBox1)
listOfWords.AddRange(patterns)
listOfWords.SelectionColor = Color.LightBlue
listOfWords.HighLightColor = Color.Yellow
If RadioButton1.Checked = True Then
listOfWords.WordsSelect()
ElseIf RadioButton2.Checked Then
listOfWords.WordsHighLight()
Else
listOfWords.DeselectAll()
End If
End Sub
This is the Class used to generate the Selections and HighLights:
Imports System.Drawing.Text
Imports System.Text.RegularExpressions
Public Class WordList
Private TextRendererFlags As TextFormatFlags =
TextFormatFlags.Top Or TextFormatFlags.Left Or TextFormatFlags.NoPadding Or
TextFormatFlags.WordBreak Or TextFormatFlags.TextBoxControl
Private textControl As RichTextBox = Nothing
Private wordsList As List(Of Word)
Public Sub New(rtb As RichTextBox)
textControl = rtb
wordsList = New List(Of Word)
ProtectSelection = False
End Sub
Public Property ProtectSelection As Boolean
Public Property HighLightColor As Color
Public Property SelectionColor As Color
Public Sub Add(word As String)
wordsList.Add(New Word() With {.Word = word, .Indexes = GetWordIndexes(word)})
End Sub
Public Sub AddRange(words As String())
For Each WordItem As String In words
wordsList.Add(New Word() With {.Word = WordItem, .Indexes = GetWordIndexes(WordItem)})
Next
End Sub
Private Function GetWordIndexes(word As String) As List(Of Integer)
Return Regex.Matches(textControl.Text, word).
OfType(Of Match)().
Select(Function(chr) chr.Index).ToList()
End Function
Public Sub DeselectAll()
If textControl IsNot Nothing Then
textControl.SelectAll()
textControl.SelectionBackColor = textControl.BackColor
textControl.Update()
End If
End Sub
Public Sub WordsHighLight()
If wordsList.Count > 0 Then
For Each WordItem As Word In wordsList
For Each Position As Integer In WordItem.Indexes
Dim p As Point = textControl.GetPositionFromCharIndex(Position)
TextRenderer.DrawText(textControl.CreateGraphics(), WordItem.Word,
textControl.Font, p, textControl.ForeColor,
HighLightColor, TextRendererFlags)
Next
Next
End If
End Sub
Public Sub WordsSelect()
DeselectAll()
If wordsList.Count > 0 Then
For Each WordItem As Word In wordsList
For Each Position As Integer In WordItem.Indexes
textControl.Select(Position, WordItem.Word.Length)
textControl.SelectionColor = textControl.ForeColor
textControl.SelectionBackColor = SelectionColor
textControl.SelectionProtected = ProtectSelection
Next
Next
End If
End Sub
Friend Class Word
Property Word As String
Property Indexes As List(Of Integer)
End Class
End Class
With a module,you can do it this way :
Imports System.Runtime.CompilerServices
Module Utility
<Extension()>
Sub HighlightText(ByVal myRtb As RichTextBox, ByVal word As String, ByVal color As Color)
If word = String.Empty Then Return
Dim index As Integer, s_start As Integer = myRtb.SelectionStart, startIndex As Integer = 0
While(__InlineAssignHelper(index, myRtb.Text.IndexOf(word, startIndex))) <> -1
myRtb.[Select](index, word.Length)
myRtb.SelectionColor = color
startIndex = index + word.Length
End While
myRtb.SelectionStart = s_start
myRtb.SelectionLength = 0
myRtb.SelectionColor = Color.Black
End Sub
<Obsolete("Please refactor code that uses this function, it is a simple work-around to simulate inline assignment in VB!")>
Private Shared Function __InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Module
Or , you can also go with this one as it will allow you to highlight multiple words at the same time :
Private Sub HighlightWords(ByVal words() As String)
Private Sub HighlightWords(ByVal words() As String)
For Each word As String In words
Dim startIndex As Integer = 0
While (startIndex < rtb1.TextLength)
Dim wordStartIndex As Integer = rtb1.Find(word, startIndex, RichTextBoxFinds.None)
If (wordStartIndex <> -1) Then
rtb1.SelectionStart = wordStartIndex
rtb1.SelectionLength = word.Length
rtb1.SelectionBackColor = System.Drawing.Color.Black
Else
Exit While
End If
startIndex += wordStartIndex + word.Length
End While
Next
End Sub
Source Hope this helps :)
This works
Public Sub HighlightText(ByVal txt As String, ByVal obj As RichTextBox)
obj.SelectionStart = 0
obj.SelectAll()
obj.SelectionBackColor = Color.White
Dim len = txt.Length
Dim pos = obj.Find(txt, 0, RichTextBoxFinds.NoHighlight)
While (pos >= 0)
obj.Select(pos, len)
obj.SelectionBackColor = Color.Yellow
If pos + len >= obj.Text.Length Then
Exit While
End If
pos = obj.Find(txt, pos + len, RichTextBoxFinds.NoHighlight)
End While
End Sub
Public Sub HighlightText(ByVal txt As String, ByVal obj As RichTextBox)
obj.SelectionStart = 0
obj.SelectAll()
obj.SelectionBackColor = Color.White
Dim len = txt.Length
Dim pos = obj.Find(txt, 0, RichTextBoxFinds.NoHighlight)
While (pos >= 0)
obj.Select(pos, len)
obj.SelectionBackColor = Color.Yellow
If pos + len >= obj.Text.Length Then
Exit While
End If
pos = obj.Find(txt, pos + len, RichTextBoxFinds.NoHighlight)
End While
End Sub
This will have a lot of code - my apologies but it's necessary to create a working sample.
I am creating an App Bar (top or left) for my application, and I cannot get transparency to work. While I am able to get the form background transparent as a form using:
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
before InitializeComponent(), and then BackColor = Color.Transparent, the App Bar background appears to be "Control." If I then set
Me.Opacity = .75
I receive a WndProc error "The parameter is incorrect."
If I then try to do something like
Me.BackColor = Color.FromArgb(25,0,0,0)
I get (basically) the same "control color" look as I do with a Transparent background.
If I try:
Me.Color = Color.Black
Me.TransparencyKey = Color.Black
I receive a similar error to the Opacity error above (a wndproc 'parameter is incorrect').
Searching has not led me anywhere useful, but I know this must be possible - the Windows taskbar is semi-transparent. I understand that they aren't exactly the same, but there must be some way to accomplish this.
In order to get this working, you should need to simply paste the below into a new Winforms app. I wrote custom auto-hide code, but am not including it to keep this as short as possible. It also won't have a way to close it unless you add a context menu, etc - but probably just stop it for simplicity.
Again, my apologies for the long code, but all is required in order to get this working:
Public Sub New()
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
InitializeComponent()
AddHandler Me.Load, AddressOf Me_Load
AddHandler Me.FormClosing, AddressOf Me_Closing
Me.FormBorderStyle = FormBorderStyle.FixedToolWindow
Me.ShowInTaskbar = False
Me.AllowTransparency = True
'Me.BackColor = Color.FromArgb(10, Color.Black)
'Me.BackColor = Color.Black
'Me.TransparencyKey = Color.Black
'Me.BackColor = Color.Transparent
'Me.Opacity = 0.5
End Sub
Private Sub Me_Load(sender As Object, e As EventArgs)
RegisterBar()
End Sub
Private Sub Me_Closing(sender As Object, e As FormClosingEventArgs)
RegisterBar()
End Sub
<StructLayout(LayoutKind.Sequential)>
Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure
<StructLayout(LayoutKind.Sequential)>
Structure APPBARDATA
Public cbSize As Integer
Public hWnd As IntPtr
Public uCallbackMessage As Integer
Public uEdge As Integer
Public rc As RECT
Public lParam As IntPtr
End Structure
Enum ABMsg As Integer
ABM_NEW = 0
ABM_REMOVE = 1
ABM_QUERYPOS = 2
ABM_SETPOS = 3
ABM_GETSTATE = 4
ABM_GETTASKBARPOS = 5
ABM_ACTIVATE = 6
ABM_GETAUTOHIDEBAR = 7
ABM_SETAUTOHIDEBAR = 8
ABM_WINDOWPOSCHANGED = 9
ABM_SETSTATE = 10
End Enum
Enum ABNotify As Integer
ABN_STATECHANGE = 0
ABN_POSCHANGED
ABN_FULLSCREENAPP
ABN_WINDOWARRANGE
End Enum
Enum ABEdge As Integer
ABE_LEFT = 0
ABE_TOP
ABE_RIGHT
ABE_BOTTOM
End Enum
Private fBarRegistered As Boolean = False
<DllImport("SHELL32", CallingConvention:=CallingConvention.StdCall)>
Private Shared Function SHAppBarMessage(dwMessage As Integer, ByRef pData As APPBARDATA) As UInteger
End Function
<DllImport("USER32")>
Private Shared Function GetSystemMetrics(Index As Integer) As Integer
End Function
<DllImport("User32.dll", ExactSpelling:=True, CharSet:=System.Runtime.InteropServices.CharSet.Auto)>
Private Shared Function MoveWindow(hWnd As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, repaint As Boolean) As Boolean
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto)>
Private Shared Function RegisterWindowMessage(msg As String) As Integer
End Function
Private uCallBack As Integer
Private Sub RegisterBar()
Dim abd As New APPBARDATA()
abd.cbSize = Marshal.SizeOf(abd)
abd.hWnd = Me.Handle
If Not fBarRegistered Then
uCallBack = RegisterWindowMessage("AppBarMessage")
abd.uCallbackMessage = uCallBack
Dim ret As UInteger = SHAppBarMessage(CInt(ABMsg.ABM_NEW), abd)
fBarRegistered = True
ABSetPos()
Else
SHAppBarMessage(CInt(ABMsg.ABM_REMOVE), abd)
fBarRegistered = False
End If
End Sub
Private Sub ABSetPos()
Dim abd As New APPBARDATA()
abd.cbSize = Marshal.SizeOf(abd)
abd.hWnd = Me.Handle
abd.uEdge = CInt(ABEdge.ABE_TOP)
If abd.uEdge = CInt(ABEdge.ABE_LEFT) OrElse abd.uEdge = CInt(ABEdge.ABE_RIGHT) Then
abd.rc.top = 0
abd.rc.bottom = SystemInformation.PrimaryMonitorSize.Height
If abd.uEdge = CInt(ABEdge.ABE_LEFT) Then
abd.rc.left = 0
abd.rc.right = Size.Width
Else
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width
abd.rc.left = abd.rc.right - Size.Width
End If
Else
abd.rc.left = 0
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width
If abd.uEdge = CInt(ABEdge.ABE_TOP) Then
abd.rc.top = 0
abd.rc.bottom = Size.Height
Else
abd.rc.bottom = SystemInformation.PrimaryMonitorSize.Height
abd.rc.top = abd.rc.bottom - Size.Height
End If
End If
' Query the system for an approved size and position.
SHAppBarMessage(CInt(ABMsg.ABM_QUERYPOS), abd)
' Adjust the rectangle, depending on the edge to which the
' appbar is anchored.
Select Case abd.uEdge
Case CInt(ABEdge.ABE_LEFT)
abd.rc.right = abd.rc.left + Size.Width
Exit Select
Case CInt(ABEdge.ABE_RIGHT)
abd.rc.left = abd.rc.right - Size.Width
Exit Select
Case CInt(ABEdge.ABE_TOP)
abd.rc.bottom = abd.rc.top + Size.Height
Exit Select
Case CInt(ABEdge.ABE_BOTTOM)
abd.rc.top = abd.rc.bottom - Size.Height
Exit Select
End Select
' Pass the final bounding rectangle to the system.
SHAppBarMessage(CInt(ABMsg.ABM_SETPOS), abd)
' Move and size the appbar so that it conforms to the
' bounding rectangle passed to the system.
MoveWindow(abd.hWnd, abd.rc.left, abd.rc.top, abd.rc.right - abd.rc.left, abd.rc.bottom - abd.rc.top, True)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = uCallBack Then
Select Case m.WParam.ToInt32()
Case CInt(ABNotify.ABN_POSCHANGED)
ABSetPos()
Exit Select
End Select
End If
MyBase.WndProc(m)
End Sub
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.Style = cp.Style And (Not &HC00000)
' WS_CAPTION
cp.Style = cp.Style And (Not &H800000)
' WS_BORDER
cp.ExStyle = &H80 Or &H8
' WS_EX_TOOLWINDOW | WS_EX_TOPMOST
Return cp
End Get
End Property
I'm not sure if the answer is going to be overriding something in CreateParams, or what, but it doesn't seem to be possible with BackColor/Transparency Key, Argb, or Opacity...
Thanks in advance.
I found many tutorials on youtube which tell us, how to use AxWindowsMediaPlayer and basic stuff like creating your own volume control using TrackBar1 for Windows Media Player Component. But now I want to ask that how we can create our own movie duration control using trackbar or a seek slider in vb.net for Windows Media Player. I have searched a lot but question still remains a question. I hope that many great developers of vb.net on this site, should tell me logic behind it
THANKS IN ADVANCE
The trick is to subclass the TrackBar control and handle the OnPaintBackground and OnPaint events.
Here's an "extra-lite" version of a control I use on some of my own projects.
This version is quite limited but it should help you get started...
Imports System.Drawing.Drawing2D
Public Class CoolTrackBar
Inherits TrackBar
Private thumbRect As Rectangle = New Rectangle(0, 0, 19, 19)
Private isOverThumb As Boolean
Private cachedValue As Integer
Private rangeRect As Rectangle = Rectangle.Empty
Private mGrooveSize As Integer = 6
Private mGrooveBorderColor As Color = Color.Gray
Private mGrooveColor As Color = Color.LightGray
Private mSelStartColor As Color = Color.Blue
Private mSelEndColor As Color = Color.Red
Public Sub New()
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.ResizeRedraw, True)
Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
End Sub
Public Property GrooveSize As Integer
Get
Return mGrooveSize
End Get
Set(value As Integer)
mGrooveSize = value
Me.Invalidate()
End Set
End Property
Public Property GrooveColor As Color
Get
Return mGrooveColor
End Get
Set(value As Color)
mGrooveColor = value
Me.Invalidate()
End Set
End Property
Public Property GrooveBorderColor As Color
Get
Return mGrooveBorderColor
End Get
Set(value As Color)
mGrooveBorderColor = value
Me.Invalidate()
End Set
End Property
Public Overloads Property TickStyle As TickStyle
Get
Return Windows.Forms.TickStyle.Both
End Get
Set(value As TickStyle)
MyBase.TickStyle = Windows.Forms.TickStyle.Both
End Set
End Property
Protected Overrides Sub OnPaintBackground(pevent As PaintEventArgs)
Dim g As Graphics = pevent.Graphics
Dim r As Rectangle = Me.DisplayRectangle
Select Case MyBase.Orientation
Case Orientation.Horizontal
rangeRect = New Rectangle(r.X + 14, r.Top, r.Width - 30, r.Height)
Case Orientation.Vertical
rangeRect = New Rectangle(r.X + 5, r.Y + 14, r.Width, r.Height - 29)
End Select
MyBase.OnPaintBackground(pevent)
DrawGroove(g)
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
Dim g As Graphics = e.Graphics
DrawThumb(g)
End Sub
Private Sub DrawGroove(g As Graphics)
Dim r1 As Rectangle
Dim r2 As Rectangle
Select Case Orientation
Case Windows.Forms.Orientation.Horizontal
r1 = New Rectangle(rangeRect.X, rangeRect.Y + (rangeRect.Height - mGrooveSize) \ 2, rangeRect.Width, mGrooveSize)
r2 = New Rectangle(r1.X, r1.Y, r1.Width * ValueToPercentage(cachedValue), r1.Height)
Case Windows.Forms.Orientation.Vertical
r1 = New Rectangle(rangeRect.X + (rangeRect.Width - mGrooveSize) / 2 - mGrooveSize \ 2, rangeRect.Y, mGrooveSize, rangeRect.Height)
r2 = New Rectangle(r1.X, r1.Y, r1.Width, r1.Height * ValueToPercentage(cachedValue))
End Select
Using b As New SolidBrush(mGrooveColor)
g.FillRectangle(b, r1)
End Using
Using p As New Pen(mGrooveBorderColor)
g.DrawRectangle(p, r1)
End Using
Using lgb As New LinearGradientBrush(r1.Location, New Point(r1.Right, r1.Bottom), mSelStartColor, mSelEndColor)
g.FillRectangle(lgb, r2)
End Using
End Sub
Private Sub DrawThumb(g As Graphics)
Dim thumb As VisualStyles.VisualStyleElement = Nothing
Select Case MyBase.Orientation
Case Orientation.Horizontal
If MyBase.Enabled Then
If isOverThumb Then
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbTop.Hot
Else
If MyBase.Focused Then
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbTop.Focused
Else
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbTop.Normal
End If
End If
Else
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbTop.Disabled
End If
Case Orientation.Vertical
If MyBase.Enabled Then
If isOverThumb Then
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbRight.Hot
Else
If MyBase.Focused Then
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbRight.Focused
Else
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbRight.Normal
End If
End If
Else
thumb = VisualStyles.VisualStyleElement.TrackBar.ThumbRight.Disabled
End If
End Select
Dim valuePercentage As Single = ValueToPercentage(cachedValue)
Dim vsr = New VisualStyles.VisualStyleRenderer(thumb)
thumbRect.Size = vsr.GetPartSize(g, VisualStyles.ThemeSizeType.Draw)
Dim pos As Integer
Select Case MyBase.Orientation
Case Orientation.Horizontal
pos = valuePercentage * rangeRect.Width
thumbRect.Location = New Point(pos + thumbRect.Width / 2 + 3, rangeRect.Y + thumbRect.Height / 2 + mGrooveSize / 4)
Case Orientation.Vertical
pos = valuePercentage * rangeRect.Height
thumbRect.Location = New Point(rangeRect.X + thumbRect.Width / 2 + mGrooveSize / 4, pos + thumbRect.Height / 2 + 3)
End Select
vsr.DrawBackground(g, thumbRect)
End Sub
Private Function ValueToPercentage(value As Integer) As Single
Dim w As Integer = MyBase.Maximum - MyBase.Minimum
Dim min = MyBase.Minimum
Dim max = MyBase.Maximum
If MyBase.Orientation = Orientation.Horizontal Then
Return (value - min) / (max - min)
Else
Return 1 - (value - min) / (max - min)
End If
End Function
Private Sub CoolTrackBar_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
If thumbRect.IntersectsWith(New Rectangle(e.Location, New Size(1, 1))) Then
isOverThumb = True
Me.Invalidate()
ElseIf isOverThumb Then
isOverThumb = False
Me.Invalidate()
End If
End Sub
Private Sub CoolTrackBar_ValueChanged(sender As Object, e As EventArgs) Handles Me.ValueChanged
cachedValue = MyBase.Value
Me.Invalidate()
End Sub
End Class
To use it, simply create a WinForms project, then create a new Class, name it CoolTrackBar and paste the code above.
You'll need to compile the solution for the control to appear on your Toolbox.