I have a question about the behavior of the combobox, more precisely about the Selected-Index-Changed-Eventhandler.
In this test project, I click on the first entry in the Combobox. Correctly, “Something happening here” appears in the output window below. But if I click on the same entry again, the event will be triggered again. That is not supposed to happen. Is there a trick to prevent this from happening? I would only think of creating a variable (oldIndex) and comparing the current index with the old one.
Edit
Public NotInheritable Class FormMain
Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ComboBox1.Items.AddRange({"1", "2", "3"})
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
If ComboBox1.SelectedIndex <> (-1) Then
Debug.WriteLine("Something hapening here")
Select Case ComboBox1.SelectedIndex
Case 0
TextBox1.Text = "1"
Case 1
TextBox1.Text = "2"
Case 2
TextBox1.Text = "3"
Case Else
Exit Select
End Select
End If
End Sub
End Class
The implementation of the SelectedIndex property actually prevents these spurious events already but it appears that selecting an item via the UI doesn't actually cause the SelectedIndex property to be set. What we can do is inherit the ComboBox class and track the SelectedIndex ourselves and then simply abort the SelectedIndexChanged event if the property value hasn't actually changed:
Public Class ComboBoxEx
Inherits ComboBox
Private _selectedIndex As Integer = Integer.MinValue
Protected Overrides Sub OnSelectedIndexChanged(e As EventArgs)
If _selectedIndex = SelectedIndex Then
'The selection hasn't actually changed.
Return
End If
_selectedIndex = SelectedIndex
MyBase.OnSelectedIndexChanged(e)
End Sub
End Class
If you add a new class to your project, add that code and then build, the new control will appear in your Toolbox. You can then add it to a form instead of a regular ComboBox. If you already have a form with a regular ComboBox on it then you can open the designer code file and change the type of the existing controls. Make sure you have a backup first, in case you make a mistake and kill your form.
There are a couple of points to note here. Firstly, the SelectionChangeCommitted event behaves the same way, so you probably ought to override the corresponding method too. If you're not using that event though, it's not too important. This code does take care of the SelectedValueChanged event though.
Secondly, I'm not sure whether there might be times when this code could cause issues, e.g. the actual item selection has changed but the SelectedIndex hasn't. Probably not an issue but something to consider.
Related
I have a group box with multiple Checkboxes(food item) and each one has a corresponding NumericUpDown control(quantity). For context, it is for a project based on a restaurant menu. I want to hide a button called btnSave whenever either a checkbox is unchecked or the quantity (NumericUpDown) is changed. I currently have btnSave.Hide under the CheckBox1_CheckedChanged and NumericUpDown1_CheckedChanged SubProcedures but I want to know if there's a way to do this when anything within this group box is changed instead of putting the code under each SubProcedure. Thanks
I think you meant .ValueChanged for the NumericUpDown control. (There is no .CheckedChanged) Although this doesn't matter much in this case, this is a good pattern for future reference. Instead of calling an event call a Sub from your events.
When you have several controls responding to a single Event handler, you can find out which control triggered the event by checking the sender parameter. Since, as you can see, sender is an Object you will have to cast it to the appropriate type to get the properties of a CheckBox.
Private Sub HideSaveButton()
btnSave.Hide
End Sub
Private Sub CheckBoxInGroupBox_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged, CheckBox2.CheckedChanged
HideSaveButton()
Dim WhichCheckBox As CheckBox = DirectCast(sender, CheckBox)
Select Case WhichCheckBox.Name
Case "CheckBox1"
MessageBox.Show("CheckBox1 has changed")
Case "CheckBox2"
MessageBox.Show("CheckBox2 has changed")
End Select
End Sub
Private Sub NumericUpDown1_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown1.ValueChanged
HideSaveButton()
End Sub
Ok I have a bit of a weird question here. I have a program similar to a currency converter (it performs a mathematical function in order to produce a value to go in another textbox). What I want it to be able to do is identify the last textbox that you edited (there are 4) and then update the rest based on what you have inputted, the user then must be able to change a different textbox to change all of them.
If anyone can get me started on how to do it or even some sample code that would be much appreciated, thanks!
Sorry if I'm not making sense, just have a look at the google currency converter and think that with two more editable boxes.
This might be what you want if I understand you correctly.
In the form class, you have a variable called lastTextBoxChangedName which keeps track of which text box was the last to be edited.
Next there is an event handler which will fire when any of the four TextBoxes are changed. This merely updates lastTextBoxChangedName.
When you have finihed editing a textbox, and tab to the next one or click on something that causes a TextBox to lose input focus, the next event handler executes. This looks at lastTextBoxChangedName to see which was the last edited TextBox and you can insert your update code to replace the comments in the Select Case block.
Public Class Form1
Dim lastTextBoxChangedName As String
Private Sub TextBox_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged, TextBox2.TextChanged, TextBox3.TextChanged, TextBox4.TextChanged
lastTextBoxChangedName = sender.name
End Sub
Private Sub TextBox1_LostFocus(sender As Object, e As EventArgs) Handles TextBox1.LostFocus, TextBox2.LostFocus, TextBox3.LostFocus, TextBox4.LostFocus
updateTextBoxes()
End Sub
Private Sub updateTextBoxes()
Select Case lastTextBoxChangedName
Case "TextBox1"
'do updates appropriate to textbox1 changed
Case "TextBox2"
'do updates appropriate to textbox2 changed
Case "TextBox3"
'do updates appropriate to textbox3 changed
Case "TextBox4"
'do updates appropriate to textbox4 changed
End Select
End Sub
End Class
However, if you already have separate event handlers for each TextBox, don't add that first event handler for TextBox_TextChanged, just add the line ..
lastTextBoxChangedName = sender.name
into each handler.
I'm writing a simple program in VB with WinForms (well, I guess so, as I have never tried anything like that before). My google-driven development attempt was going pretty well until I tried to make a ComboBox control show one of its items by default.
So there is ComboBox1 with two items ("Item A" and "Item B") added through graphical interface (property Items in Properties panel). I go to Form1_Load event description in the code window and add the following line:
ComboBox1.SelectedItem = 0
That is supposed to make "Item A" the default item preselected when the program starts. But it doesn't work. What am I doing wrong?
That's because you are using 0(an integer) on ComboBox.SelectedItem, but ComboBox.Selected item is not an index to an element, it's an actual object.
This is how you use ComboBox.SelectedItem:
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ComboBox1.Items.Add("Item A")
ComboBox1.Items.Add("Item B")
ComboBox1.SelectedItem = "Item A"
End Sub
End Class
I am writing a program using a database for customers and technicians. The main form (CustomerIncidents) has a toolstripbutton that opens a different form to (SearchByState) where the user inputs a state code and looks for any incidents.
If the user clicks into one of the datagrid cells I want that customers information to be stored in the TAG so that when the form is closed using the OK button that it will show back up in the main form (CustomerIncidents).
Edited 03/11/14 12:21pm
The problem is in the Main Form. When I click the OK button in the Second Form it tries to convert the DialogResult Button to a String. I can't figure out how to fix it.
Customer Form (Main Form) Opens to Secondary Form
Private Sub btnOpenState_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnOpenState.Click
Dim frmSearchState As New FindCustomer
----->>Dim selectedButton As DialogResult = frmSearchState.ShowDialog()
If selectedButton = Windows.Forms.DialogResult.OK Then
CustomerIDToolStripTextBox.Text = frmSearchState.Tag.ToString
End If'
Search By State Form (Secondary Form) Or "Child Form"
Private Sub btnOk_Click(message As String, ByVal e As DataGridViewCellEventArgs) Handles btnOk.Click
message = CustomersDataGridView.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString
Me.Tag = message
Me.DialogResult = DialogResult.OK
End Sub
The click event for a button does not have a DataGridViewCellEventArgs parameter, and will throw an exception when you try to use it.
You don't need to use the Tag property since you can just create your own property.
In your child form, create a property called GridValue:
Private Sub btnOk_Click(sender As Object, e As EventArgs) Handles btnOk.Click
If dgv.CurrentCell Is Nothing OrElse dgv.CurrentCell.Value Is Nothing Then
MessageBox.Show("A cell needs to be selected.")
Else
Me.DialogResult = DialogResult.OK
End If
End Sub
Public ReadOnly Property GridValue As String
Get
Return dgv.CurrentCell.Value.ToString
End Get
End Property
In your parent form, you can now access your information:
Using frmSearchState As New FindCustomer
If frmSearchState.ShowDialog(Me) = DialogResult.Ok Then
CustomerIDToolStripTextBox.Text = frmSearchState.GridValue
End If
End Using
My personal approach for doing this kind of stuff is to create a public property in the child form, having the same type as the DATA you want to take back to your main form. So instead of storing DataGridView's reference in Tag property, you should really be storing the actual value that was there in the cell that the user clicked on.
For example, if your DGV cell has a string value in it, you could do something like:
Public Readonly Property StateName As String
Get
If YourDGV.SelectedCell IsNot Nothing Then
Return YourDGV.SelectedCell.Value
Else
Return ""
End If
End Get
End Property
(I have written that code by hand, so there may be some syntax problems, but you should be able to get the idea.)
You can now use ShowDialog() in the main form to bring up this child form and upon OK or Cancel, you could check the value of StateName property of your child form to get this value. The thing to remember here is that closing a form doesn't dispose off all its constituent controls and properties and therefore you can access them even after the form has finished ShowDialog() call.
Good day everyone.
I need your help in this project I am into (a Visual Basic program with no database.) It just contains a Datagridview, a Textbox, and three buttons (an "Add" Button, a "Edit" and an "Update" Button).
1 . Is there any way (like using "for loop") to automatically assign DataGridView1.Item("item location") to the one edited and be updated?
2 . Or is it possible to just click an item in the Datagridview then it will be edited at that without passing it to a Textbox, and to be updated at that.
The DataGridViewCellEventArgs variable (e in the method stub the designer will generate for you) of the double click event of the cell has RowIndex and ColumnIndex properties which refer to the position of the cell you clicked.
Save those (in a class variable possibly or a local one if that's all you need) and then refer to them when you update the cell in your DataGridView, possibly like this MyDataGridView.Item(e.ColumnIndex, e.RowIndex) or MyDataGridView.Rows(e.RowIndex).Cells(e.ColumnIndex) where e is the variable from the double click event handler.
For you cell double click event you could have something like this:
Private Sub DataGridView1_CellDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellDoubleClick
Using myEditor As New frmCellEditor(Me.DataGridView1.Item(e.ColumnIndex, e.RowIndex).Value)
If myEditor.ShowDialog() = DialogResult.OK Then
Me.DataGridView1.Item(e.ColumnIndex, e.RowIndex).Value = myEditor.NewCellValue
End If
End Using
End Sub
This will call a new instance of your editor and get a value from you. For the purpose of this demo I have made a form like this:
Public Class frmCellEditor
Public NewCellValue As Integer
Public Sub New(ByVal CurrentCellValue As Object)
InitializeComponent()
Me.TextBox1.Text = CStr(CurrentCellValue)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.NewCellValue = CInt(Me.TextBox1.Text)
Me.DialogResult = DialogResult.OK
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Call Me.Close()
End Sub
End Class
Which just has two buttons (Button1 = OK, Button2 = Cancel). When you click OK, it just returns the value 1 which then gets set as the value of the cell.
This is a VERY simplistic example, but it should provide you the basics of what you are trying to do.
UPDATE:
I updated the code for the editor interface so it will include handling for passing the value back and forth from the form with your datagridview.
In your project, make a new form called frmCellEditor. This forms has to have two buttons and a textbox (Make sure that the programmatic names match!). Replace the code with the code listed above. You will have to add Imports System.Windows.Forms above the class as well.
Amend the event handler for the cell double click event of your datagrid to pass the cell value when frmCellEditor is constructed (the line going ... New frmCellEditor(...).
How many columns does your DataGridView has?
Based on how you populate your DataGridView, I'll assume only 1.
Declare this on top of your form
Dim i as Integer
On your btnUpdate_Click Event (Just combine your Edit and Update button into One)
SELECT CASE btnUpdate.Text
Case "Update"
With DataGridView1
'Check if there is a selected row
If .SelectedRows.Count = 0 Then
Msgbox "No Row Selected for Update"
Exit Sub
End If
i = .CurrentRow.Index 'Remember the Row Position
Textbox1.Text = .item(0 ,i).value 'Pass the Value to the textbox
.Enabled = False 'Disable DataGridView to prevent users from clicking other row while updating.
btnUpdate.Text = "Save"
End With
Case Else 'Save
DatagridView1.Item(0,i).Value = Textbox1.Text
btnUpdate.Text = "Update"
END SELECT
Thanks for those who contributed to finding answers for this thread. I have not used your solutions for now (maybe some other time). After some research, I've found an answer for problem 2 (more user friendly at that):
2 . Or is it possible to just click an item in the Datagridview then
it will be edited at that without passing it to a Textbox, and to be
updated at that.
Here's what i did:
in Private Sub Form1_Load, just add:
yourDataGridView.EditMode = DataGridViewEditMode.EditOnEnter
in Private Sub yourDataGridView_(whatever event here: DoubleCellClick, CellContentClick, etc.) add:
DataGridView1(e.ColumnIndex, e.RowIndex).[ReadOnly] = False
DataGridView1.BeginEdit(False)