Disabling buttons one at a time - vb.net

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

Related

VB.NET DataGridView End Edits and Cell Movment

I have created a DGV adding CheckBoxColumns and TextBoxColumns. When the user enters a text value in to a TextBoxCell it then takes 2 mouse clicks to move to the next clicked cell, is there a way to allow a single mouse click to allow the next value to be entered in the selected cell straight away?
Also trying to use the Arrow Up/Down keys after entering a value doesn't move to the relevant new cell? When i use the arrow key is seems to end the edit but doesn't move to the next cell, it looks like the DGV loses focus as the mouse cursor appears.
I need both of these to work and to allow for immediate edit/ data entry as each text cell is for user input and not being able to use mouse keys to quickly navigate or having to double click with the mouse is causing a lot of wasted time.
Any help regarding these two issue would be much appreciated!
Additional Info::
This is what the grid looks like::
Grid Layout Image
The first 2 columns are data populated from a previous form and the data is held in a DataTable, the rest of the columns are added via the program
With the 3 text columns it takes two clicks on the next cell to move to the requested cell and start to edit if something has been typed, again if something has been typed in a cell and i use the arrow keys doesn't seem to take you to the next cell, it leaves the cell put doesn't start to edit or put the cursor in the expected cell.
What i am looking for is when i have entered a value in to the Text Columns and click on the next cell it starts to edit straight away or if i use an arrow key it takes you to the relevant cell and starts the edit.
Below is the full code for the form i have an issue with, i have also added a screenshot of the DGV Properties as i bet all of this is fixed by something simple.
DGV Settings
Public Class frmAppTest
Public DTApp As DataTable = New DataTable("Application")
Private Sub frmAppTest_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Call InitializeAppTable()
'Add each column required for user input (inc dropdown option and "Select All" option)
Call Add_ChkColumn("Team Crest", "TEAM_CREST")
Call AddSAChkBox(dgvApp, 186, 57, 2, "TC")
Call AddCrestSelect(dgvApp, 165, 33, "Crest")
Call Add_ChkColumn("Chest Logo", "CHEST_LOGO")
Call AddSAChkBox(dgvApp, 246, 57, 3, "CL")
Call AddSponsorSelect(dgvApp, 225, 33, "Chest")
Call Add_ChkColumn("Back" & vbCr & "Top", "BACK_TOP_LOGO")
Call AddSAChkBox(dgvApp, 306, 57, 4, "BT")
Call AddSponsorSelect(dgvApp, 285, 33, "BackTop")
Call Add_ChkColumn("Back Bottom", "BACK_BOTTOM_LOGO")
Call AddSAChkBox(dgvApp, 366, 57, 5, "BB")
Call AddSponsorSelect(dgvApp, 345, 33, "BackBottom")
Call Add_ChkColumn("Left Sleeve", "LEFT_SLEEVE_LOGO")
Call AddSAChkBox(dgvApp, 426, 57, 6, "LS")
Call AddSponsorSelect(dgvApp, 405, 33, "LeftSlv")
Call Add_ChkColumn("Right Sleeve", "RIGHT_SLEEVE_LOGO")
Call AddSAChkBox(dgvApp, 486, 57, 7, "RS")
Call AddSponsorSelect(dgvApp, 465, 33, "RightSlv")
Call Add_TxtColumn("Player Name", "PLAYER_NAME")
Call AddColourSelect(dgvApp, 528, 33, "Name")
Call Add_TxtColumn("Player Number", "PLAYER_NUMBER")
Call AddColourSelect(dgvApp, 638, 33, "Number")
Call Add_TxtColumn("Player Initials", "PLAYER_INITIALS")
Call AddColourSelect(dgvApp, 748, 33, "Initials")
Call FormatDGVApp()
'Populate default data
Call PopulateDetails()
End Sub
Private Sub AddCrestSelect(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal CboName As String)
Dim cbo As New ComboBox
cbo.Name = "cboLogo" & CboName
'The box size
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Visible = True
cbo.Items.Clear()
cbo.Items.Add("Embro")
cbo.Items.Add("Heat")
cbo.Size = New Size(55, 14)
cbo.SelectedIndex = 0
cbo.Location = New System.Drawing.Point(XLocation, YLocation)
cbo.BackColor = Color.White
theDataGridView.Controls.Add(cbo)
End Sub
Private Sub AddSponsorSelect(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal CboName As String)
Dim cbo As New ComboBox
cbo.Name = "cboLogo" & CboName
'The box size
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Visible = True
cbo.Items.Clear()
cbo.Items.Add("Single")
cbo.Items.Add("Multi")
cbo.Size = New Size(55, 14)
cbo.SelectedIndex = 0
cbo.Location = New System.Drawing.Point(XLocation, YLocation)
cbo.BackColor = Color.White
theDataGridView.Controls.Add(cbo)
End Sub
Private Sub AddColourSelect(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal CboName As String)
Dim cbo As New ComboBox
cbo.Name = "cboColour" & CboName
'The box size
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Visible = True
cbo.Items.Clear()
cbo.Items.Add("White")
cbo.Items.Add("Black")
cbo.Items.Add("Royal")
cbo.Items.Add("Navy")
cbo.Items.Add("Yellow")
cbo.Items.Add("Red")
cbo.Size = New Size(100, 14)
cbo.SelectedIndex = 0
cbo.Location = New System.Drawing.Point(XLocation, YLocation)
cbo.BackColor = Color.White
theDataGridView.Controls.Add(cbo)
End Sub
Private Sub InitializeAppTable()
DTApp.Columns.Add("Size", GetType(String))
DTApp.Columns.Add("Price", GetType(String))
dgvApp.DataSource = DTApp
End Sub
Private Sub FormatDGVApp()
Dim xCol As New DataGridViewColumn
dgvApp.Columns("Size").Width = 70
dgvApp.Columns("Size").ReadOnly = True
dgvApp.Columns("Size").SortMode = DataGridViewColumnSortMode.NotSortable
dgvApp.Columns("Size").HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.Columns("Size").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.Columns("Price").Width = 70
dgvApp.Columns("Price").ReadOnly = True
dgvApp.Columns("Price").SortMode = DataGridViewColumnSortMode.NotSortable
dgvApp.Columns("Price").HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.Columns("Price").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
dgvApp.ColumnHeadersHeight = 74
dgvApp.Font = New Font("Arial", 7)
End Sub
Private Sub PopulateDetails()
Dim AppRow As DataRow
Dim i As Integer
For i = 0 To 4
AppRow = DTApp.NewRow
AppRow("Size") = "S"
AppRow("Price") = "5.00"
DTApp.Rows.Add(AppRow)
Next
End Sub
Private Sub Add_ChkColumn(ByVal Header As String, ByVal Name As String)
Dim AddColumnLabelExport As New DataGridViewCheckBoxColumn
With AddColumnLabelExport
.HeaderText = Header
.Name = Name
.Width = 60
.HeaderCell.Style.Alignment = DataGridViewContentAlignment.TopCenter
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
.SortMode = DataGridViewColumnSortMode.NotSortable
End With
dgvApp.Columns.Add(AddColumnLabelExport)
End Sub
Private _IsSelectAllChecked As Boolean
Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
Dim cbx As New CheckBox
cbx.Name = "SelectAll" & ChkName
'The box size
cbx.Size = New Size(14, 14)
cbx.Tag = TagNo
cbx.Location = New System.Drawing.Point(XLocation, YLocation)
cbx.BackColor = Color.White
theDataGridView.Controls.Add(cbx)
AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
End Sub
Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
Me._IsSelectAllChecked = True
Dim cbx As CheckBox
cbx = DirectCast(sender, CheckBox)
Dim theDataGridView As DataGridView = cbx.Parent
Dim rowId As Integer = cbx.Tag
For Each row As DataGridViewRow In dgvApp.Rows
row.Cells(rowId).Value = cbx.Checked
Next
theDataGridView.EndEdit()
Me._IsSelectAllChecked = False
End Sub
Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
dgvApp.EndEdit()
Dim dataGridView As DataGridView = DirectCast(sender, DataGridView)
If Not Me._IsSelectAllChecked Then
Select Case e.ColumnIndex
Case 2
If dataGridView.Rows(e.RowIndex).Cells(2).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllTC"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(2).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllTC"), CheckBox).Checked = isAllChecked
End If
Case 3
If dataGridView.Rows(e.RowIndex).Cells(3).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllCL"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(3).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllCL"), CheckBox).Checked = isAllChecked
End If
Case 4
If dataGridView.Rows(e.RowIndex).Cells(4).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllBT"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(4).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllBT"), CheckBox).Checked = isAllChecked
End If
Case 5
If dataGridView.Rows(e.RowIndex).Cells(5).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllBB"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(5).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllBB"), CheckBox).Checked = isAllChecked
End If
Case 6
If dataGridView.Rows(e.RowIndex).Cells(6).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllLS"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(6).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllLS"), CheckBox).Checked = isAllChecked
End If
Case 7
If dataGridView.Rows(e.RowIndex).Cells(7).Value = False Then
'When any single CheckBox is unchecked, uncheck the header CheckBox.
DirectCast(dataGridView.Controls.Item("SelectAllRS"), CheckBox).Checked = False
Else
'When any single CheckBox is checked, loop through all CheckBoxes to determine
'if the header CheckBox needs to be unchecked.
Dim isAllChecked As Boolean = True
For Each row As DataGridViewRow In dataGridView.Rows
If row.Cells(7).Value = False Then
isAllChecked = False
Exit For
End If
Next
DirectCast(dataGridView.Controls.Item("SelectAllRS"), CheckBox).Checked = isAllChecked
End If
End Select
End If
dgvApp.CurrentCell = Nothing
End Sub
Private Sub DataGridView_CurrentCellDirtyStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim dataGridView As DataGridView = DirectCast(sender, DataGridView)
If TypeOf dataGridView.CurrentCell Is DataGridViewCheckBoxCell Then
dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
Private Sub Add_TxtColumn(ByVal Header As String, ByVal Name As String)
Dim AddColumnLabelExport As New DataGridViewTextBoxColumn
With AddColumnLabelExport
.HeaderText = Header
.Name = Name
.Width = 110
.HeaderCell.Style.Alignment = DataGridViewContentAlignment.TopCenter
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
.SortMode = DataGridViewColumnSortMode.NotSortable
End With
dgvApp.Columns.Add(AddColumnLabelExport)
End Sub
Private Sub btnCancelLine_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancelLine.Click
Me.Close()
End Sub
End Class
Well… the updated code explains numerous issues you may be having. The big picture is that you need to take a little more care when subscribing to some events. It is not difficult to create a problem if you do not take care in checking when and how often your subscribed to events are firing.
Example, in your current code you have the method…
Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
Dim cbx As New CheckBox
cbx.Name = "SelectAll" & ChkName
'The box size
cbx.Size = New Size(14, 14)
cbx.Tag = TagNo
cbx.Location = New System.Drawing.Point(XLocation, YLocation)
cbx.BackColor = Color.White
theDataGridView.Controls.Add(cbx)
AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
End Sub
This code drops a check box into the grid and is called six (6) times. Once for each of the “select all” Header check boxes. Is what is incorrect are the last two lines of code…
AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
This is odd because each time the method is called a NEW handler is added to the grid. In other words, the code is subscribing to the same GRID event’s six (6) times.
If you put a couple of debug statements at the beginning and the end of the events, you will see that the events will obligingly fire five more times than needed.
Bottom line… these GRID events only need to be subscribed to ONCE. I suggest moving the code to the Forms Load event or some method that is only called once.
Next, your current issue relates to the fact that when you type text into the other non-check box cells, the wired-up grid events are still firing and you really do not want this when editing those non-check box cells. So, a simple solution is to check and make sure that when those events fire… that we do nothing if the cell is not one of the check box cells in the grid.
Currently the code is using the grids CellValueChanged event and its CurrentCellDirtyStateChanged event. In the current cell dirty state changed event, the code simply “commits” the edit to the cell which in turn will fire the grids CellValueChanged event where the code checks the check box cells. As you note… the current cell dirty state changed event will fire BEFORE the actual cell value is changed and this is the purpose of the commit so we can immediately see the changes in the grid.
I suggest you use the grids, CellContentClick event instead of the CurrentCellDirtyStateChanged event. This event will fire when the user clicks into a cell and starts to edit the cell. It will fire when the user clicks a check box cell and changes its value. We do not need to do anything other than check if the cell is a check box cell and commit the edit. If the cell is NOT a check box cell then do nothing. This event may look something like…
Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
Return
End If
Debug.WriteLine("DataGridView_CellContentClick <- Enter")
dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
Debug.WriteLine("DataGridView_CellContentClick -> Leave")
End Sub
This will work when the check boxes are changed and it will ignore the non-check box cells, however if the user is editing a non-check box cell, then the grids CellValueChanged event will still fire if the non-check box cells value changed.… So similar to the above code, we need to check in the grids CellValueChanged event to see which cell value changed. If it is NOT a check box cell that changed, then we do not want to do anything. Therefore you could add the same check using the e.RowIndex and e.ColumnIndex variables… in the grids CellValueChanged event… Something like…
Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
Return
End If
….. ‘rest of code
Another issue which I wish I had a good answer for is that… if you call the HeaderCheckBox_Click event that fires when the user clicks one of the “select all” header check boxes, then you may note that… IF the user has clicked into a check box cell and changed its value, then this will make that cell the grids CurrentCell and for some reason, it will not change its value as expected. I was only able to code a workaround that simply changes the grids CurrentCell to some other cell, then sets it back after the code changes the check box cell values. The changes I made may look something like…
Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
Debug.WriteLine("HeaderCheckBox <- Enter")
Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
Dim cbx As CheckBox
cbx = DirectCast(sender, CheckBox)
Dim rowId As Integer = cbx.Tag
RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
For Each row As DataGridViewRow In dgvApp.Rows
row.Cells(rowId).Value = cbx.Checked
Next
AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
Debug.WriteLine("HeaderCheckBox -> Leave")
End Sub
You may note that just before the For Each loop is started through the grid rows, we remove the GRIDS CellValueChanged event to prevent it from firing. We do not want it to fire at this time since we are checking or unchecking all the check boxes, therefore no checking is needed to see if they are all checked or not checked.
In my tests, these changes allowed the non-check box cells to work as expected. I hope this makes sense and helps. You may want to consider simplifying this. Example, in each of the Select/Case sections the code is doing the same thing with a different column index. I suggest creating a simple method that checks if all the check boxes are checked in a given column and return true/false. And I hope you do not plan on any horizontal scrolling of the grid as this will create a lot of work for you to scroll the added combo boxes, labels and check boxes you added to the grid… just a heads up.

show tooltip on disabled button inside multiple controls

I would like to display the tooltip on a disabled button, I've tried the code below but it looks not to be working.
I think that's because the button is inside a group box and the group box inside a panel, the panel is inside the form.
the code reported would work only if the button is directly on the form..
Private toolTipShown As Boolean = False
Private Sub TimeWorks_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseMove
Dim parent = TryCast(sender, Control)
If parent Is Nothing Then
Return
End If
Dim ctrl = parent.GetChildAtPoint(e.Location)
If ctrl IsNot Nothing Then
If ctrl.Visible AndAlso toolTip1.Tag Is Nothing Then
If Not toolTipShown Then
Dim tipstring = toolTip1.GetToolTip(ctrl)
toolTip1.Show(tipstring.Trim(), ctrl, ctrl.Width / 2, ctrl.Height / 2)
toolTip1.Tag = ctrl
toolTipShown = True
End If
End If
Else
ctrl = TryCast(toolTip1.Tag, Control)
If ctrl IsNot Nothing Then
toolTip1.Hide(ctrl)
toolTip1.Tag = Nothing
toolTipShown = False
End If
End If
End Sub
Any suggestions to make the code working?

Disable textboxes with the corresponding checkboxes in VB.net

Public Class Form1
Dim controlNames() As String = {"Tea", "Cola", "Coffee", "Orange", "Water", "VanillaCone", "VanillaShake", "StrawberryShake", "ChocolateMilkshake", "Fries", "Salad", "Hamburger", "OnionRings", "ChickenSalad", "FishSandwich", "CheeseSandwich", "ChickenSandwich"}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
For Each ctrl As Control In Me.Panel2.Controls
If TypeOf ctrl Is TextBox Then
ctrl.Enabled = False
End If
Next
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs)
For i = 0 To 16
For Each ctrl As Control In Me.Panel2.Controls
If TypeOf ctrl Is CheckBox Then
If ctrl.Name = "chk" & controlNames(i) Then
If DirectCast(ctrl, CheckBox).CheckState = CheckState.Checked Then
If TypeOf ctrl Is TextBox Then
If ctrl.Name = "txt" & controlNames(i) Then
ctrl.Enabled = True
End If
End If
End If
End If
End If
Next
Next
End Sub
I have a VB.NET assignment and I am attempting to enable textboxes based on whether or not the checkbox with the same name was checked.
This is my code so far and it obviously doesn't work. What I want to do essentially:
All textboxes start as disabled. Then, textboxes only get enabled if the corresponding checkboxes are checked. For example, if chkTea is checked, then it enables txtTea.
I am aware I can copy paste something like "if chkTea = checked then txt.tea = enabled". I do not want to do this as this seems like a poor way to go about this. I want to know if I can do something like my barely readable code shows.
If you're renaming all your controls to something different to the default name with a number, this code should work just fine.
You dont need a timer, it just fires when any of the CheckBoxes have their state changed.
In the example code, I've created a CheckedChanged handler and set it to handle some of the CheckBoxes (You'll want to add all those you want to handle). If you click on any of these CheckBoxes, the handler will fire. The handler then passes which checkbox has been changed to the SyncTextBoxWithCheckBoxState method. This then finds the matching textbox using the FindMatchingCheckBox method and sets the .Enabled state of the Text box to the same as the .Checked state of the CheckBox
Private Sub chkTea_CheckedChanged(sender As Object, e As EventArgs) Handles chkTea.CheckedChanged, chkCoffee.CheckedChanged, chkCola.CheckedChanged, chkOrange.CheckedChanged, chkTea.CheckedChanged, chkVanillaCone.CheckedChanged, chkVanillaCone.CheckedChanged, chkWater.CheckedChanged
SyncTextBoxWithCheckBoxState(CType(sender, CheckBox))
End Sub
Private Sub SyncTextBoxWithCheckBoxState(chkBox As CheckBox)
Dim txtBox As TextBox = FindMatchingTextBox(chkBox)
txtBox.Enabled = chkBox.Checked
End Sub
Private Function FindMatchingTextBox(chkbox As CheckBox) As TextBox
For Each ctrl As Control In Panel2.Controls
If ctrl.GetType Is GetType(TextBox) And ctrl.Name.Contains(chkbox.Name.Substring(3)) Then
Return CType(ctrl, TextBox)
End If
Next
Return Nothing
End Function
EDIT
To have the code target more than one panel, add all the checkboxes you want to detect to the event handler like before, and in the FindMatchingTextBox method, just add another loop around the existing one to loop through each panel. Like so ..
Private Function FindMatchingTextBox(chkbox As CheckBox) As TextBox
'This is the new loop which loops through the two panels. It's
'a bit quick and dirty, but it works
For Each pnl As Panel In New Panel() {Panel2, Panel3}
'In this line note that the reference to Panel2 now refers to pnl
For Each ctrl As Control In pnl.Controls
If ctrl.GetType Is GetType(TextBox) And ctrl.Name.Contains(chkbox.Name.Substring(3)) Then
Return CType(ctrl, TextBox)
End If
Next
'End point of the new loop
Next
Return Nothing
End Function
After you've looped through each checkbox, you are only concerned with that checkbox, so when you find the correct checkbox, you need to then RELOOP back through all the controls on the form checking if they are the corresponding textbox.
Something like the below should work, or at the very least start you off down the right path:
Dim controlNames() As String = {"Tea", "Cola", "Coffee", "Orange", "Water", "VanillaCone", "VanillaShake", "StrawberryShake", "ChocolateMilkshake", "Fries", "Salad", "Hamburger", "OnionRings", "ChickenSalad", "FishSandwich", "CheeseSandwich", "ChickenSandwich"}
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
For Each ctrl As Control In Me.Panel2.Controls
If TypeOf ctrl Is TextBox Then
ctrl.Enabled = False
End If
Next
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs)
For i = 0 To 16
For Each ctrl As Control In Me.Panel2.Controls
If TypeOf ctrl Is CheckBox Then
If ctrl.Name = "chk" & controlNames(i) Then
If DirectCast(ctrl, CheckBox).CheckState = CheckState.Checked Then
For Each ctrl As Control In Me.Panel2.Controls
If TypeOf ctrl Is TextBox Then
If ctrl.Name = "txt" & controlNames(i) Then
ctrl.Enabled = True
End If
End If
Next
End If
End If
End If
Next
Next
End Sub

Button should change text every two presses

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

ComboBox SelectChangeCommited fires off with first keyboard input

I have a ComboBox. My issue is when I click on element 1, then start typing the name of a element 2, the first letter of my element 2 gets added. This only happens on the key down after a click event.
Process:
Click on ComboBox
Type in a name
Click on the elements name - element gets added to my grid
Type in another name - ISSUE - first element that matches the first letter I input gets added to the grid
Clicking and the enter key on a single selected element works fine.
My AutoCompleteMode is set to Append and AutoCompleteSource is ListItems
The solution I thought to control a click is in my SelectionChangeComitted function. If I can control the flag isCLick to only be set true when we know that a element from the combo box is clicked by the mouse. MouseClick will return true if the user clicks on the overall ComboBox therefore will always be true if we're dealing with this particular ComboBox. Since the parameter is EventArgs I cannot directly cast it to MouseEventArgs, would be great if I could.
To sum up what my problem is: I need to replace isClick boolean in my SelectionChangeCommitted to validate if the SelectionChangeCommitted was triggered by a click or not.
Is that possible in this case? Would there be any alternatives to bypass this issue?
I have event handler code below, the sequence of the call is: MouseClick -> PreviewKeyDown -> SelectionChangeCommitted -> SelectedValueChanged
http://pastebin.com/9zWGHbWE
AddHandler CType(ctl, ComboBox).PreviewKeyDown, Sub(sender As Object, e As PreviewKeyDownEventArgs)
If e.KeyCode = Keys.Down OrElse e.KeyCode = Keys.Up OrElse e.KeyCode = Keys.Right OrElse e.KeyCode = Keys.Left Then
e.IsInputKey = False
AddToGrid = False
isArrowKey = True
isClick = False
ElseIf e.KeyCode = Keys.Enter OrElse e.KeyCode = Keys.Tab Then
AddToGrid = True
isArrowKey = False
isClick = False
'Check if row already exists before insert a new one.
If Not RowExists(DirectCast(ctl, Control).Tag, DirectCast(ctl, ComboBox).Text.ToUpper()) Then AppendGrid(sender)
Else
AddToGrid = False
isArrowKey = False
isClick = True
End If
End Sub
AddHandler CType(ctl, ComboBox).SelectedValueChanged, Sub(sender As Object, e As EventArgs)
'MessageBox.Show("SelectionValueChanged")
If CType(sender, ComboBox).SelectedValue Is Nothing Then Return
If Not AddToGrid Then Return
'Check if row already exists before insert a new one.
If Not RowExists(DirectCast(ctl, Control).Tag, DirectCast(ctl, ComboBox).SelectedValue.ToString().ToUpper()) Then
AppendGrid(sender)
End If
End Sub
AddHandler CType(ctl, ComboBox).SelectionChangeCommitted, Sub(sender As Object, e As EventArgs)
'MessageBox.Show("SelectionChangedCommitted")
If CType(sender, ComboBox).SelectedValue Is Nothing Then Return
If isArrowKey Then Return 'if an arrow was used, then don't add it to the grid
If isClick Then
AddToGrid = True
End If
'Check if row already exists before insert a new one.
If Not RowExists(DirectCast(ctl, Control).Tag, DirectCast(ctl, ComboBox).SelectedValue.ToString().ToUpper()) AndAlso AddToGrid Then
AppendGrid(sender)
End If
End Sub
AddHandler CType(ctl, ComboBox).MouseClick, Sub(sender As Object, e As MouseEventArgs)
'MessageBox.Show("MouseClick")
isClick = True
AddToGrid = True
End Sub
It seems like everything here works fine for you except when a click is executed. I would recommend you to reset the click after you use it. Try the code below in your SelectionChangeCommitted.
If isClick Then
AddToGrid = True
isClick = false
End If