Button should change text every two presses - vb.net

I'm trying to create a Tic Tac Toe game using Visual Basic. After pressing the button, the button should firstly turn to "O" then the next press should be a "X" however it seems that it continues placing "O".
Dim turn As Boolean
turn = True 'true = X turn, false = Y turn
Dim b As Button
b = DirectCast(sender, Button)
If (turn) Then
b.Text = "O"
Else
b.Text = "X"
turn = Not turn <<< This seems to not to be working...
b.Enabled = False
End If

You need to declare the turn variable outside the method, so that it is a member of the class and not a local variable that is recreated each time.
Also, you should flip the state after each change, not only when an X is placed, and the same for disabling the button.
Example:
Dim turn As Boolean = True 'true = X turn, false = Y turn
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim b As Button = DirectCast(sender, Button)
If turn Then
b.Text = "O"
Else
b.Text = "X"
End If
turn = Not turn
b.Enabled = False
End Sub

Related

Creating a confirmation which switches back to previous radio button if user cancels

So I currently have a form where I have 4 teams, each with 4 teammates. This cannot change.
my form GUI
Initially when the program starts, it automatically selects the "current value" radio button which displays labels with blank data, you can then select "edit values" which can be saved by pressing the big green apply button. This stores the data under a 2d array named stTeamName, which the labels reference whenever you select "current value" again.
There are 4 radio buttons which allow you to switch between the teams.
My problem is, when I change the radio buttons, any data put into the text labels that has not been saved will be deleted.
I solved this problem by adding a message box which will stop the data in the labels from changing unless the user gives confirmation to do so.
However, my only problem is that after I cancel this, the radio button stays at the current selection which does not help.
I solved this issue by using rbTeamName1.Checked = True (rbTeamname 1 to 4 being the radio buttons named team 1-4) to switch back to the previous state.
This of course only goes back to rbTeamname1 no matter what, so I employed an 1d array and select case to help solve this issue.
"""
Dim RadioCounter(1) As Integer
the code below simply gives the default Radio button value on load.`
Sub DeclareRadio(RadioButton)
RadioButton(1) = 1
End Sub
"""
Whenever a radio button is successfully selected, RadioCounter(1) is set to the number 1-4 respectively. Whenever a new radio button is selected, the value of RadioCounter(1) is moved to RadioCounter(0) and the current radio button's value is to RadioCounter(1).
If the user selects no, RadioCounter(0)'s value is referenced and a select case selects the radio button.
(Please note, RadioTeam() is used as a counter to store Team name and Teammate name string variables correctly within the 2d array: stTeamName. I will most likely merge this with RadioCounter as soon as I get this problem fixed)
"""
Private Sub rbTeamName1_CheckedChanged(sender As Object, e As EventArgs) Handles rbTeamName1.CheckedChanged
'note txtTeamName starts at 0, however RadioTeam starts at 1, RadioTeam = 0 has no team designated to it.
'this function is for swapping teams via radio button, each time, a confirmation is given to make sure the user saves the current team, or risk deletion of the inputted data.
RadioCounter(1) = RadioCounter(0)
stTeamname(0, RadioTeam) = txtTeamName.Text
stTeamname(1, RadioTeam) = txtMate1.Text
stTeamname(2, RadioTeam) = txtMate2.Text
stTeamname(3, RadioTeam) = txtMate3.Text
stTeamname(4, RadioTeam) = txtMate4.Text
Dim Dialog As DialogResult
Dialog = MessageBox.Show("Are you sure you want to change team? Any unsaved changes will be lost. If this box shows on starting the program, just press no", "Change team?", MessageBoxButtons.YesNo)
If Dialog = DialogResult.No Then
'need to run function to check previous result of radio button'
Select Case RadioCounter(0)
Case 1
rbTeamName1.Checked = True
Case 2
rbTeamName2.Checked = True
Case 3
rbTeamName3.Checked = True
Case 4
rbTeamName4.Checked = True
End Select
ElseIf Dialog = DialogResult.Yes Then
RadioCounter(1) = 1 'this equals the respective number for each radio button'
RadioTeam = 1
txtTeamName.Text = stTeamname(0, RadioTeam)
txtMate1.Text = stTeamname(1, RadioTeam)
txtMate2.Text = stTeamname(2, RadioTeam)
txtMate3.Text = stTeamname(3, RadioTeam)
txtMate4.Text = stTeamname(4, RadioTeam)
lblTeamName.Text = stTeamname(0, RadioTeam)
lblMate1.Text = stTeamname(1, RadioTeam)
lblMate2.Text = stTeamname(2, RadioTeam)
lblMate3.Text = stTeamname(3, RadioTeam)
lblMate4.Text = stTeamname(4, RadioTeam)
End If
End Sub
"""
....Or it should do that. But it doesnt. Im not sure why, but when I force it to check a specific radio button, by removing the select case and only running rbTeamName4.Checked = True it will work perfectly fine, theres something about the select case that simply doesnt run the code.
Also, despite stTeamname(0, RadioTeam) = txtTeamName.Text etc. being given before the messagebox shows, if I do not press the apply button to save, press another team button, then press no, it will still not save those parameters, which I find extremely weird.
What is the ideal solution to this?
To clarify:
If a user writes team names and teammates and does not press the apply button to save and then changes team by selecting another radio button, a text box should appear asking whether they want to switch team and lose the data (Yes) or stay on the previous radio button to edit and apply the data (No).
Upon pressing no, the program automatically switches the current radio button to the previous selection to make it seem as if no radio button was selected in the first place.
And im not sure if I have to make it obvious but im new to both stackoverflow and VB.net in general. Thank you in advance.
Essentially what you are asking for is dirty form checking. The basic principle is that you need to compare the value to what it was previously.
So for example:
When the TextBox values change, set a form level boolean variable to true
When you go to switch teams or current/edit you would check if that form level boolean variable is true
If the variable is true then you would display the prompt, otherwise just do the action
After the action, reset the global level boolean variable
There are some caveats, for example, when you toggle between current/edit the check changed will fire twice: once for the current radio button and once for the edit. But you just need to work around those edge cases.
Here is a largely untested example for you to go off of:
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Public Class Form1
Private _team1 As Team
Private _team2 As Team
Private _team3 As Team
Private _team4 As Team
Private _isFormDirty As Boolean = False
Private _selectedTeamRadioButton = RadioButtonTeam1
Private Sub RadioButtonCurrent_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButtonCurrent.CheckedChanged
If (RadioButtonCurrent.Checked AndAlso _isFormDirty AndAlso MessageBox.Show("By doing this, you will lose all unsaved changes. Are you sure?", "Dirty Form", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.No) Then
RadioButtonEdit.Checked = True
Return
End If
If (Not RadioButtonCurrent.Checked) Then
Return
End If
TextBoxTeamName.Enabled = False
TextBoxTeammate1.Enabled = False
TextBoxTeammate2.Enabled = False
TextBoxTeammate3.Enabled = False
TextBoxTeammate4.Enabled = False
ResetTeam()
End Sub
Private Sub RadioButtonEdit_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButtonEdit.CheckedChanged
TextBoxTeamName.Enabled = True
TextBoxTeammate1.Enabled = True
TextBoxTeammate2.Enabled = True
TextBoxTeammate3.Enabled = True
TextBoxTeammate4.Enabled = True
End Sub
Private Sub RadioButtonTeamChanged(sender As Object, e As EventArgs) Handles RadioButtonTeam1.CheckedChanged, RadioButtonTeam2.CheckedChanged, RadioButtonTeam3.CheckedChanged, RadioButtonTeam4.CheckedChanged
If (_selectedTeamRadioButton IsNot DirectCast(sender, RadioButton) AndAlso _isFormDirty AndAlso MessageBox.Show("By doing this, you will lose all unsaved changes. Are you sure?", "Dirty Form", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.No) Then
_selectedTeamRadioButton.Checked = True
Return
End If
If (_selectedTeamRadioButton IsNot DirectCast(sender, RadioButton)) Then
Return
End If
_selectedTeamRadioButton = DirectCast(sender, RadioButton)
ResetTeam()
End Sub
Private Sub TextBoxTeamValueChanged(sender As Object, e As EventArgs) Handles TextBoxTeamName.TextChanged, TextBoxTeammate1.TextAlignChanged, TextBoxTeammate2.TextAlignChanged, TextBoxTeammate3.TextAlignChanged, TextBoxTeammate4.TextAlignChanged
_isFormDirty = True
End Sub
Private Sub ButtonApply_Click(sender As Object, e As EventArgs) Handles ButtonApply.Click
Select Case True
Case RadioButtonTeam1.Checked
_team1 = New Team() With {
.Name = TextBoxTeamName.Text,
.Teammate1 = TextBoxTeammate1.Text,
.Teammate2 = TextBoxTeammate2.Text,
.Teammate3 = TextBoxTeammate3.Text,
.Teammate4 = TextBoxTeammate4.Text
}
Case RadioButtonTeam2.Checked
_team2 = New Team() With {
.Name = TextBoxTeamName.Text,
.Teammate1 = TextBoxTeammate1.Text,
.Teammate2 = TextBoxTeammate2.Text,
.Teammate3 = TextBoxTeammate3.Text,
.Teammate4 = TextBoxTeammate4.Text
}
Case RadioButtonTeam3.Checked
_team3 = New Team() With {
.Name = TextBoxTeamName.Text,
.Teammate1 = TextBoxTeammate1.Text,
.Teammate2 = TextBoxTeammate2.Text,
.Teammate3 = TextBoxTeammate3.Text,
.Teammate4 = TextBoxTeammate4.Text
}
Case RadioButtonTeam4.Checked
_team4 = New Team() With {
.Name = TextBoxTeamName.Text,
.Teammate1 = TextBoxTeammate1.Text,
.Teammate2 = TextBoxTeammate2.Text,
.Teammate3 = TextBoxTeammate3.Text,
.Teammate4 = TextBoxTeammate4.Text
}
End Select
_isFormDirty = False
End Sub
Private Sub ButtonDeleteTeam_Click(sender As Object, e As EventArgs) Handles ButtonDeleteTeam.Click
If (MessageBox.Show("Are you sure you want to delete this team?", "Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.No) Then
Return
End If
_isFormDirty = False
Select Case True
Case RadioButtonTeam1.Checked
_team1 = New Team()
Case RadioButtonTeam2.Checked
_team2 = New Team()
Case RadioButtonTeam3.Checked
_team3 = New Team()
Case RadioButtonTeam4.Checked
_team4 = New Team()
End Select
ResetTeam()
End Sub
Private Sub ResetTeam()
Dim selectedTeam As Team = Nothing
Select Case True
Case RadioButtonTeam1.Checked
selectedTeam = _team1
Case RadioButtonTeam2.Checked
selectedTeam = _team2
Case RadioButtonTeam3.Checked
selectedTeam = _team3
Case RadioButtonTeam4.Checked
selectedTeam = _team4
End Select
If (selectedTeam IsNot Nothing) Then
TextBoxTeamName.Text = selectedTeam.Name
TextBoxTeammate1.Text = selectedTeam.Teammate1
TextBoxTeammate2.Text = selectedTeam.Teammate2
TextBoxTeammate3.Text = selectedTeam.Teammate3
TextBoxTeammate4.Text = selectedTeam.Teammate4
Else
TextBoxTeamName.Clear()
TextBoxTeammate1.Clear()
TextBoxTeammate2.Clear()
TextBoxTeammate3.Clear()
TextBoxTeammate4.Clear()
End If
End Sub
End Class
Public Class Team
Implements INotifyPropertyChanged
Private _name As String
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
If (_name <> value) Then
_name = value
NotifyPropertyChanged()
End If
End Set
End Property
Private _teammate1 As String
Public Property Teammate1 As String
Get
Return _teammate1
End Get
Set(value As String)
If (_teammate1 <> value) Then
_teammate1 = value
NotifyPropertyChanged()
End If
End Set
End Property
Private _teammate2 As String
Public Property Teammate2 As String
Get
Return _teammate2
End Get
Set(value As String)
If (_teammate2 <> value) Then
_teammate2 = value
NotifyPropertyChanged()
End If
End Set
End Property
Private _teammate3 As String
Public Property Teammate3 As String
Get
Return _teammate3
End Get
Set(value As String)
If (_teammate3 <> value) Then
_teammate3 = value
NotifyPropertyChanged()
End If
End Set
End Property
Private _teammate4 As String
Public Property Teammate4 As String
Get
Return _teammate4
End Get
Set(value As String)
If (_teammate4 <> value) Then
_teammate4 = value
NotifyPropertyChanged()
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class

Making labels in charts selectable/editable in VB

Just like in excel for a graph you can double click on a title, series name, or axis label and then you are able to type in that space. What do I need to do so I can do that for my charts? Clueless on where to start. Seems I may have to create a custom label?
Use the HitTest method of the chart.
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Private Sub Chart1_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles Chart1.MouseDoubleClick
Dim h As HitTestResult = Chart1.HitTest(e.X, e.Y) 'Perform the HitTest with the mouse position that was clicked
If h.ChartElementType = ChartElementType.AxisTitle OrElse _
h.ChartElementType = ChartElementType.Axis Then 'Check the type of the element of the chart that was clicked
Dim s As String = InputBox("Please enter a new title!", "", h.Axis.Title) 'Prompt for a new title
If s <> "" Then h.Axis.Title = s 'Assign the new title
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i = 1 To 20 'Put some data in the chart to make the axes visible
Chart1.Series(0).Points.AddXY(i, i ^ 2)
Next
End Sub
End Class
You basically use the mouse location for the HitTest method. The ChartElementType defines what element was clicked at the position. If it's the title of the axis or the axis itself it will prompt you with an InputBox for a new title and assigns this title.
The InputBox is pretty old and shouldn't really be used but I was lazy and it works :-)
Apparently, what I'm referring to lives in DataVisualization.Charting.TextAnnotation. Along with that one, are many more type of annotations.
A TextAnnotation allows: AnchorMoving, Moving, PathEditing, Resizing, Selecting, and TextEditing. All of which I'm looking for.
Simple setup:
Dim anno As New DataVisualization.Charting.TextAnnotation
anno.AllowTextEditing = true
anno.AllowSelecting = true
anno.AllowMoving = true
anno.AllowResizing = true
anno.x = 50
anno.y = 50
anno.text = "Your Text"
chart.annotations.add(xAxisAnno)

Disabling buttons one at a time

My partner and I are trying to figure out how to disable a button one at a time. We're making a program in Visual Studio Express 2012 that will disable a button once it is typed in a textbox. For example, we have five letters placed seperately on five different buttons If we were to put the letter "D" on the textbox, the button that contains that specific letter will be disabled. We're using the code
If e.KeyCode = Keys.D Then
Button1.Enabled = False
End If
Now that works, BUT if there were two or more buttons that has the same letters, all of them disables because then the code will be :
If e.KeyCode = Keys.D Then
Button1.Enabled = False
End If
If e.KeyCode = Keys.D Then
Button2.Enabled = False
End If
My problem would be in what way could I distinguish those buttons that has the same letter from one another so that when I type the letter on a textbox, only one button disables and when I type it in again, another button containing the same letter disables. Thanks!
Assuming all of the buttons are not in child panels:
If e.KeyCode = Keys.D Then
For Each b As Button In Me.Controls.OfType(Of Button)()
If b.Text.Contains("D") AndAlso b.Enabled Then
b.Enabled = False
Exit For
End If
Next
End If
This will recursively iterate all controls on the form looking for buttons and disable them based on the characters and number of characters entered into the textbox:
Private Sub textBox1_TextChanged(sender As Object, e As System.EventArgs)
Dim text As String = TryCast(sender, TextBox).Text.ToLower()
For Each b As Button In GetAllButtons(Me)
b.Enabled = True
Next
For Each c As Char In text
Dim count As Integer = text.Count(Function(cc) cc = c)
For i As Integer = 0 To count - 1
For Each b As Button In GetAllButtons(Me).Where(Function(x) x.Text.ToLower().Contains(c.ToString())).Take(count).ToList()
b.Enabled = False
Next
Next
Next
End Sub
Private Function GetAllButtons(control As Control) As List(Of Button)
Dim allButtons As New List(Of Button)()
If control.HasChildren Then
For Each c As Control In control.Controls
allButtons.AddRange(GetAllButtons(c))
Next
ElseIf TypeOf control Is Button Then
allButtons.Add(TryCast(control, Button))
End If
Return allButtons
End Function

Combining mouseEvents on label in VB.NET

I want to combine MouseEnter with MousePressed on a label.
Public Sub populateGrid()
lblTest.BackColor()
lblTest.BackColor = System.Drawing.Color.Red
gbWorkflow.Controls.Add(lblTest)
For j As Integer = 1 To 40
For i As Integer = 1 To 20
Dim L As New Label
L.Size = New Size(30, 30)
L.Text = "L:" + i.ToString + j.ToString
L.BackColor = Color.AliceBlue
Dim x, y As Integer
Dim loc As Point = gbWorkflow.Location
y = loc.Y * (i * 8) '- (gbWorkflow.Height + L.Size.Height) * i
x = loc.X * (j * 8)
L.Location = New Point(x, y)
gbWorkflow.Controls.Add(L)
AddHandler L.MouseEnter, AddressOf L_Enter
AddHandler L.MouseLeave, AddressOf L_Leave
Next
Next
End Sub
Private Sub L_Enter(ByVal sender As Object, ByVal e As System.EventArgs)
Dim TheLabel As Label = CType(sender, Label)
TheLabel.BackColor = Color.Red
End Sub
My idea was to create a method that triggers on MouseDown and changes the value of a boolean variable. I would then use that variable as a condition to apply the changes to the labels. However, that doesnt seem to work...
How can I achieve this in the best way? Or, at all?
Pseudo-code:
When mouse enter label:
if left mousebutton is pressed then
do stuff with the label
Edit:
It also has to work when the mousebutton is still pressed and the cursor is dragged over several labels. All the labels that the cursor crosses while the left button is pressed should be changed.
The MouseDown event delivers what you are after. Sample code:
Private Sub L_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs)
If (e.Button = Windows.Forms.MouseButtons.Left) Then
'Do stuff
End If
End Sub
Adding the event handler:
AddHandler L.MouseDown, AddressOf L_MouseDown
--- UPDATE
As said, you cannot accomplish directly what you want but there are many alternative ways to deliver an equivalent performance. For example:
Boolean flag indicating whether one of the target labels has been clicked (MouseDown) + MouseEnter performing the modifications only if this flag is true. Sample code:
Private LWasClicked As Boolean = False
Private Sub L_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs)
If (e.Button = Windows.Forms.MouseButtons.Left And Not LWasClicked) Then
LWasClicked = True
End If
End Sub
Private Sub L_MouseEnter(sender As Object, e As System.EventArgs)
If (LWasClicked) Then
'Do stuff
End If
End Sub
With the code above, you can "activate the editing" by just clicking on any label (or on a specific one); once it is activated, you can just pass the mouse over any label and the actions will be performed. You will also have to set an event to de-activate this behaviour (example: new Click/MouseDown). As you can see, this delivers an equivalent performance to what you want and is compatible with how events work.
CLARIFICATION: I think that this (or any other alternative on these lines) delivers an excellent performance. If still you don't want that and prefer to do everything with the mouse-button pressed, you would have to rely on something different (e.g., position of the mouse on the screen, analysis triggered by other means; or even events from different threads). What is clear is that what you aim cannot be accomplished with one-thread events of different controls (a new event cannot be started before the previous one has ended).

VB.Net: Using and addressing buttons in TableLayoutPanel

Hi I have this TablelayoutPanel setup currently in my program to create a grid of buttons that will later correspond to a specific column and row:
'****INITIALISES TABLE LAYOUT INTO FORM******
Dim ColCount, RowCount As Integer
'Later change so that values are automatically calculated
ColCount = 5
RowCount = 5
'*********Copy and pasted from site as example, CHANGE LATER*******
Haztable = New TableLayoutPanel
Haztable.AutoScroll = True
Haztable.Dock = DockStyle.Fill
Haztable.ColumnCount = ColCount
Haztable.RowCount = RowCount
For rowNo As Integer = 0 To Haztable.RowCount - 1
For columnNo As Integer = 0 To Haztable.ColumnCount - 1
'Dim ctrl As Control = New Button
'ctrl.Text = String.Format("{0} {1},{2}", ctrl.GetType().Name, columnNo, rowNo)
'ctrl.Size = New Size(20, 20)
'Haztable.Controls.Add(ctrl, columnNo, rowNo)
Dim buttonname As String
buttonname = "B" & columnNo & rowNo
Dim button As Control = New Button
button.Size = New Size(70, 20)
button.Name = buttonname
button.Text = buttonname
Haztable.Controls.Add(button, columnNo, rowNo)
AddHandler button.Click, AddressOf buttonname_Click
Next
Next
Me.Controls.Add(Haztable)
Call buttonfind()
And this all works, creating a grid of buttons, much like the layout of an excel spreadsheet.
The buttons are named according to their XY position (e.g. the button in (1,1) would be called "B11") but the problem is I can't seem to work out how I can address these buttons i.e
*If B(X.Y) is clicked then save boolean value that button at X,Y is pressed.
It would be great to have one algorithm to scan and check if any buttons have been pressed instead of using "Select Case" for each button.
I would just create the buttons in the designer but for my full code i'm going to need 1000+ buttons and that seems an inefficient way to do so.
Your buttonname_Click should have a Sender object which is the Button that you Clicked just cast it to a Button and check the name then.
Private Sub buttonname_Click(sender As System.Object, e As System.EventArgs)
Dim btn As Button = CType(sender, Button)
Select Case btn.Name
Case "B11"
'Do something
Case "B12"
'Do Something esle
'...........
End Select
End Sub
Based on your last statement see if this works you may need to build an Array or a List if you need to reference the Text elsewhere in your Program
Private Sub buttonname_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim btn As Button = CType(sender, Button)
If btn.Text = "H" Then
btn.Text = "M"
ElseIf btn.Text = "M" Then
btn.Text = "L"
ElseIf btn.Text = "L" Then
btn.Text = ""
Else
btn.Text = "H"
End If
End Sub
You have add clicked event and handle each of them by using their row and column number. Make yourself a new button first so that you can access row and column numbers with spending no effort to parse column and row numbers from the controls name:
Public Class NewButton
Inherits Button
Public Row, Column, ClickCount as Integer
End Class
Now create and handle:
Public Class Form1
Sub addbuttons()
Dim newbut As New NewButton
newbut.Name = "B12"
newbut.Row = "1"
newbut.Column = "2"
'and other properties...
AddHandler newbut.Click, AddressOf clicked
Me.Controls.Add(newbut)
End Sub
Sub clicked(sender As System.Object, e As System.EventArgs)
Dim x As NewButton = DirectCast(sender, NewButton)
If x.Column = 2 And x.Row = 1 Then
x.ClickCount += 1
End If
End Sub
End Class