VBA Textbox's change affect second textbox and vice versa - vba

On my form I have more textboxes, but two of them are special. It's textbox with name tbVolume and textbox with name tbWeight.
If I change tbVolume, by other parameters is computed value for tbWeight, but when I added same procedure to tbWeight (to compute value for tbVolume), it creates some kind of cyclically link, because change of volume changes weight, change of weight changes volume, etc...
Does it exist some argument of tbVolume_Changed() / tbWeight_Changed() which can tell to procedure if value is changed by user or by application?
Or do you have another idea how to solve this twin-textbox problem?

Oh.. Finally it was so easy, only two new booleans and everything works fine:
Dim editingVolumeByApp As Boolean
Dim editingWeightByApp As Boolean
Private Sub tbVolume_Change()
If editingVolumeByApp = False Then
'...
editingWeightByApp = True
tbWeight.Value = finalVolume * CDbl(tbMatDensi.Value)
editingWeightByApp = False
'...
End If
End Sub
Private Sub tbWeight_Change()
If editingWeightByApp = False Then
'...
editingVolumeByApp = True
tbVolume.Value = finalVolume * CDbl(tbMatComplCoef.Value)
editingVolumeByApp = False
'...
End If
End Sub
And it works fine :-)

Instead of tbWeight_Change() or tb_Volume_Change() you could use the following:
Private Sub tbWeight_Exit(ByVal Cancel As MSForms.ReturnBoolean)
' do everything you want to do
End Sub
and of course:
Private Sub tbVolume_Exit(ByVal Cancel As MSForms.ReturnBoolean)
' do everything you want to do
End Sub
You change the value of the textbox, exit it via tab or mouse click and the other value of the textbox will change.
If you want a parallel output while writing in the textbox you can use an extra textbox just for displaying the calculated value.

Related

How do I get the textbox to enable after a certain amount of text is inputted?

So my next question(i know i know ive had a lot of questions already but im learning and my teachers suck)
but I am trying to get the textbox to go to readonly after a certain amount of text has been entered. I know how to make it a read only textbox but only after Ive had one set of data entered. i need it to be readonly after 7 days of data has been entered
I've tried inputtextbox.enabled = false
'Validating if user input is a number or not
Dim output As Integer
If Not Integer.TryParse(InputTextbox.Text, output) Then
MessageBox.Show("ERROR! Data must be a number")
InputTextbox.Text = String.Empty
Else
UnitsTextbox.AppendText(Environment.NewLine & InputTextbox.Text)
InputTextbox.Text = String.Empty
End If
InputTextbox.Enabled = False
I'm expecting it to disable after the user has entered 7 days worth of data but it only disables after one day of data is entered
Since the entries to UnitsTextbox are all done in code, this TextBox can be set to read only at design time.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim output As Integer
If Not Integer.TryParse(InputTextbox.Text, output) Then
MessageBox.Show("ERROR! Data must be a number")
Else
UnitsTextbox.AppendText(Environment.NewLine & InputTextbox.Text)
End If
'Moved this line outside of the If because it happens either way
InputTextbox.Text = String.Empty
If UnitsTextbox.Lines.Length >= 7 Then
Button2.Enabled = False
End If
End Sub
Here's some simple psuedocode
Private Sub InvalidateTextbox(sender As TextBox, e As KeyEventArgs) Handles TextBox1.KeyUp, TextBox2.KeyUp
'FOR ANY TEXTBOX YOU WANT TO CONTROL WITH THIS SUB, ADD AN ADDITIONAL HANDLE.
If Strings.Len(sender.Text) > 7 Then
'^SIMPLE CONDITIONAL, CHECKING IF THE LENGTH IS MORE THAN SEVEN CHARACTERS, MODIFY THIS TO SUIT YOUR NEEDS.
sender.Enabled = False
'^IF THE CONDITIONAL IS TRUE, DEACTIVATE THE CONTROL, IF THAT IS WHAT YOU ARE LOOKING FOR.
sender.ReadOnly = true
'^IF YOU WANT READONLY,NOT ENABLED/DISABLED.
End If
End Sub
This code will execute every time a key is pressed while the text boxes are active. What is after "Handles" defines what events will trigger the sub.
sender becomes the textbox object that triggered the sub. e holds all the event arguments for the keyboard, so you can evaluate things like which key was pressed and other neat things.
There was some confusion on if you wanted enabled/disabled or readonly, both options included.

Issue setting the picture property of an image on a VBA form

I have 3 pictures on a vba form.
The images are (picture1, picture2, and selectedImage)
The VBA sets either picture1 or picture2 equal to selectedImage. Picture1 and Picture2 initialize to (none), and selectedImageis set to a local bitfile.
Private Sub updatePicture(position As Integer)
'This code works when un-commented out and run
'Picture1.Picture = selectedImage.Picture
'This code does not update the images
If position = 1 Then
Picture1.Picture = selectedImage.Picture
ElseIf position = 2 Then
Picture2.Picture = selectedImage.Picture
End If
End Sub
I debugged and confirmed position = 1, and the line 'Picture1.Picture = selectedImage.Picture' is being run, yet the image is not being updated...
Any help would be welcome. Using excel 2013.
This type of problem is often associated with the fact that a change to an Image picture does not fire an event. Assuming that you're definitely passing a 1 or 2 into the procedure (you should check for sure that you are), there are several ways you could handle the repaint issue:
One would be to add the Repaint method at the bottom of your code, so your procedure would read:
If position = 1 Then
Picture1.Picture = selectedImage.Picture
ElseIf position = 2 Then
Picture2.Picture = selectedImage.Picture
End If
Me.Repaint
However, this will repaint your entire Userform and can cause flicker if you are updating the picture rapidly (eg on a progress monitor or multiple click handling) or have a huge Userform.
The other way would be to create an event and run your new picture through that event. I can't say this from a position of authority, but my impression is that the Event will coerce a repaint and appears only to refresh that part of the Userform where a change is detected, so my experience is that this method is far smoother.
The way you'd do this is very simple. You insert a new Class and suitably name it (I called mine clsPicHandler). The code within that class could be a little as this:
Option Explicit
Public Event PictureChanged(img As MSForms.Image, pic As Object)
Public Sub SetPicture(img As MSForms.Image, pic As Object)
If img Is Nothing Or pic Is Nothing Then Exit Sub
If img.Picture Is pic Then Exit Sub
RaiseEvent PictureChanged(img, pic)
End Sub
The code behind your Userform, then, would trigger and handle the event, like so:
Option Explicit
Private WithEvents mPicHandler As clsPicHandler
Public Sub UpdatePicture(position As Integer)
If position = 1 Then
mPicHandler.SetPicture Picture1, selectedImage.Picture
ElseIf position = 2 Then
mPicHandler.SetPicture Picture2, selectedImage.Picture
End If
End Sub
Private Sub mPicHandler_PictureChanged(img As MSForms.Image, pic As Object)
img.Picture = pic
DoEvents
End Sub
Private Sub UserForm_Initialize()
Set mPicHandler = New clsPicHandler
End Sub

Disable Button If Combox Input Deleted

In my project, I have a few textbox inputs, and some combo boxes with maybe 2 indexed items on a form. There's a button I'm disabling on load if no input is supplied to both textbox inputs, and it works great even if I delete out any text. However, I'm having issues with forcing the combo box to behave in the same manner. This work however:
Private Sub cboPickShirts_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboPickShirts.SelectedIndexChanged
InputCheck_3 = True
If cboPickShirts.SelectedIndex < 0 Then
InputCheck_3 = False
End If
If InputCheck_3 = False Then
btnInputResult.Enabled = False
ElseIf InputCheck_3 = True Then
btnInputResult.Enabled = True
End If
End Sub
I have InputCheck_3 set up as a global variable in a Public Module. On form load, I'm disabling my button and it doesn't enable until I select one of the indexed items. My struggle to get the button disable again if any combo box text is entered and deleted out, leaving it null or empty. Any thoughts on what I'm missing or what I can add to get results? I guess I need a variable or event to notice the change (entering & deletion of text).
The problem you are having is that your SelectedIndexChanged event is not being triggered when you remove the selected item from your ComboBox. I would use the TextChanged event of your TextBox's and ComboBox and give it a common handler and check it that way. Something like this
Private Sub TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged, TextBox2.TextChanged, cboPickShirts.TextChanged
EnableCheck()
End Sub
Private Sub EnableCheck()
btnInputResult.Enabled = (String.IsNullOrEmpty(TextBox1.Text) And String.IsNullOrEmpty(TextBox2.Text) And ComboBox1.SelectedIndex = -1)
End Sub
You can also check that the comboBox is NullorEmpty the same way as the textbox's. As it stands right now the combobox will be enabled when the text no longer matches a selection.
One line code
btnInputResult.Enabled = If((cboPickShirts.SelectedIndex<0),False, True)

Using combobox from class

I have some questions on using of combobox.
1) I need to reference combobox from class like this:
If Me.ActiveControl.GetType Is GetType(ComboBox) And combodroppeddown = False) Then
something...
End If
From Here I need right from the AND to check if this combobox is dropped down but I don't know how.
2) My actual type of combobox is of "DropDownList" type.
Problem is that if I drop it down and type with up/down keys the value is changed according to selected row. If I then press ESC then last value stays as choosen what is not wanted.
Is here way to return original value from moment of dropping in case if I press ESC when list is dropped?
How to do that?
Here is closer look to my xCombo subclass to get help in second question...
Public Class xAutoCombo
Inherits ComboBox
Private entertext As String
Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)
If e.Control Then ' this dropped a list with keyboard
If e.KeyCode = Keys.Down Then
Me.DroppedDown = True
End If
End If
'' this should return value back if ESC was pressed
'' but don't work!
If Me.DroppedDown And e.KeyCode = Keys.Escape Then
Me.Text = entertext
End If
MyBase.OnKeyDown(e)
End Sub
Protected Overrides Sub OnDropDown(ByVal e As System.EventArgs)
entertext = Me.Text '' this remember text in moment of droping
MyBase.OnDropDown(e)
End Sub
EDIT:
Here I found one issue in functionality which I like to be solved.
When combo is dropped and I navigate through list by keyboard and then press with mouse to form (or outside of combo) it closes a list and set value which is last been selected.
Instead of that I would like that combo set his new value ONLY on click with mouse to list or with pressing Enter key with keyboard.
Examine the DroppedDown property, but it seemed like you have other things you wanted to do while dropped down.
Dim cbo As ComboBox
If Me.ActiveControl.GetType Is GetType(ComboBox) then
cbo=Ctype(Me.ActiveControl, ComboBox)
' make it easier to refernece
if cbo.DroppedDOwn then
....
End iF
End if
' Note:
Ctype(Me.ActiveControl, ComboBox).DroppedDown
' should work, but the above is easier to read and use since you apparently
' will have other things to do/override with it
Note also that I think one of the three combobox dropdown types does not use/support the DroppedDown property.
For the rest of your question, which I dont entirely follow, you could also store the last selected item and restore it in similar fashion. Overriding windows default behavior though, is rarely a good idea because you are creating something the user has never encountered before.
EDIT
To change Escape functionality from CLOSE DROPDOWN to ABORT CHOICE:
NOTE: I just used a std cbo, refs will need to be changed to MyBase, events to On...
Private selectedindex As Integer = -1
Private bEsc As Boolean = False
Private Sub cbo_Enter(....
' reset tracking vars...might not be needed
selectedindex = -1
bEsc = False
End Sub
Private Sub cbo_DropDown(...
' capture starting state
selectedindex = cbo.SelectedIndex
bEsc = False
End Sub
KeyDown:
If cbo.DroppedDown AndAlso e.KeyCode = Keys.Escape Then
bEsc = True
e.Handled = True
' this MUST be last!
cbo.DroppedDown = False
End If
Private Sub cbo_DropDownClosed(...
' rest cbo.selectedindex if escape was pressed
If bEsc Then
' note: SelectedIndexChanged event will fire and any code there will run
' may need to qualify with If Esc = False...
cbo.SelectedIndex = selectedindex
End If
End Sub

Edit Update DatagridView VB.Net (No Database)

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)