Buttonless Temperature Converter in Visual Basic? - visual-studio

Public Class Form1
Private Sub FahrenheitTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FahrenheitTextBox.TextChanged
CelsiusTextBox.Text = 5 / 9 * (Val(FahrenheitTextBox.Text) - 32)
End Sub
Private Sub CelsiusTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CelsiusTextBox.TextChanged
FahrenheitTextBox.Text = (9 / 5) * Val(CelsiusTextBox.Text) + 32
End Sub
End Class
So, that's my code. Our assignment was to create a temperature converter that worked without buttons- so, when I typed in the Fahrenheit box, it would automatically convert to Celsius in the Celsius box. The way I determined doing this was by using an if statement- my professor said I was not allowed to do that because we have not learned it yet. My problem right now is that the two TextChanged events are competing with each other- so when I type in one, it calculates the other, and then calculates the other and keeps going and messing up the numbers. I am not allowed to use an 'if statement'. His reply to my question if we could use an if statement was "Adding IF statement would solve the problem together with TextChanged event. However, at this moment, let’s assume that we do not know IF statement (with Boolean values). In addition, it does not have to be complicated if you choose the right event for textbox." So...I don't really know how to proceed without an if statement or an event that would require pressing a key. I emailed him with my problems (stating I did not know how to proceed without the if statement or pressing a button (which would take away the automatic conversion)) He also stated, when I inquired about a KeyPress event, "You are so close to the answer. KeyPress event asks you to press and hold the key before it works. How about other key events? I am sure you will get the solution out soon."
Could anyone please help me?

Have you tried KeyDown event? Here is an example: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keydown(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2

Related

Odd problem. Step through works, runtime doesn't

Morning all.
I've seen numerous posts about this problem on here, but nothing specific to my situation so would appreciate some assistance with this.
Essentially, I'm attempting to rearrange the order of tabpages on a tabcontrol, based on the order of entries in a listbox (lbxBuildings). The number of pages always matches the number of listbox entries, and their text values also match up.
Now I've written the following code which works perfectly when I step through it, but doesn't work (or error) at runtime.
Private Sub cmdBlgSetDown_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdBlgSetDown.Click
'Make sure our item is not the last one on the list.
If lbxBuildings.SelectedIndex < lbxBuildings.Items.Count - 1 Then
'Insert places items above the index you supply, since we want
'to move it down the list we have to do + 2
Dim I = lbxBuildings.SelectedIndex + 2
Dim j As Integer = lbxBuildings.SelectedIndex
Dim NTP As TabPage = TabControl2.TabPages(j)
TabControl2.TabPages.Insert(I, NTP)
TabControl2.SelectedIndex = I - 1
TabControl2.TabPages.Remove(NTP)
lbxBuildings.Items.Insert(I, lbxBuildings.SelectedItem)
lbxBuildings.Items.RemoveAt(lbxBuildings.SelectedIndex)
lbxBuildings.SelectedIndex = I - 1
End If
End Sub
The rearranging of the listbox is controlled by two buttons (one up and one down) and that all works fine, so I now want the same buttons to rearrange the tabpages as well. There may well be information already entered on these pages prior to their being rearranged, so deleting one and adding a new one won't work for me (as far as I can tell).
My 'solution' of adding in a copy in the correct place then deleting the original makes perfect sense to me and as I say, it works when I step through. But at runtime, it seems to skip right over the 'insert' line and just deletes the orignal tab.
All suggestions welcome.
Many thanks!
Well after a load more digging I found mention of this specific problem on a C# site. More digging still and it turns out that there's a bug in the program with the following workaround.
Dim h As IntPtr = TabControl2.Handle
Call this 'before' doing anything with the tabcontrol and it works as expected. It's a bit odd but there it is.

Missing events in properties for VB6

I am a self taught amateur Visual Basic programmer. My programs are for myself, friends and some non-profit organizations. I have a situation that I thought would be reasonably simple but doesn't seems the case.
I have a text box array of 6 elements (1 -6) named "txtBilled" .When the value is entered into any but element 6 I want to add the values in 1-5 and put result in element 6. My problems start due to the fact that the properties for the text array does not provide for a lost focus option. Searching the inter provides statements that this is normal, others say the "Lost Focus" should always be there.
As a second approach I tried to use the validate element. Never used this before created a sub as follows that I found on the web.
Private sub txtBilled__Validate(Cancel as Boolean)
Found that the Validate event is also not included in the properties for the array
I am using VB6 version 8176 under Windows 10.
Any ideal as to what I am doing incorrectly would be appreciated.
Can you not create your own LostFocus sub using the Index of the textbox array?
Private Sub txtBilled_LostFocus(Index As Integer)
Dim i As Integer
dim sngTotal As Single
' Calculate sum only if not in last textbox
If Index <> uBound(txtBilled) Then
For i = LBound(txtBilled) to UBound(txtBilled) - 1
sngTotal = sngTotal + txtBilled(i)
Next i
txtBilled(uBound(txtBilled)) = sngTotal
End If
End Sub

Confusing with Approaching If statements and Debugging

I'm extremely new to Visual Studio 2010.
For my class, I need to make a calculator. The requirement for this problem is that if A or B is zero, then for the program to not actually calculate A or B; instead, the program assigns the value of the variable that is not zero to eh result, and if they're both zero, then just make the answer zero.
Here is my code:
Public Class Form1
Private Sub AddBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddButton.Click
Dim A As Decimal
Dim B As Decimal
Dim Result As Decimal
A = ABox.Text
B = BBox.Text
A = Decimal.Parse(ABox.Text)
B = Decimal.Parse(BBox.Text)
If (ABox.Text = 0) Then
ResultLabel.Text = BBox.Text
End If
If (BBox.Text = 0) Then
ResultLabel.Text = ABox.Text
End If
If (BBox.Text = 0 And ABox.Text = 0) Then
ResultLabel.Text = 0
End If
Result = A + B
ResultLabel.Text = Result.ToString("N2")
End Sub
My questions are as follows:
Are if statements good for this, or would Try/Catch be better?
Since the answer will automatically be right, even if the code is incorrect (e.g. 9+0 will be nine regardless, whether or not the If or Try/Catch actually works), they key is proper step by step debugging. What's the optimal way to do this? I had the one menu displaying everything step by step with how the program functioned before, but for some reason, I can't seem to find the window after I closed it. I want to see it step by step as I debug with break points.
Any other tips on good syntax for these type of conditional operators?
Sorry for sounding stupid; I couldn't find the topics on here that would cover this type of problem.
OK...first off, you don't really need the Ifs at all. (Identity Property of Addition: For any x, x + 0 = x.) Any error that's going to happen is going to happen before you even get to them, so the only thing you've done is say that if ABox parses to zero and BBox's value is something like "5.000000", all those zeros will get copied into the result. Likewise if the boxes are reversed.
Also, if you use Decimal.TryParse instead of Decimal.Parse, un-number-like stuff in the input boxes won't kill your program. They'll just get turned into 0.
You'd get similar results (minus the extra zeros) with some code like
Private Sub AddBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddButton.Click
Dim A as Decimal, B as Decimal
Decimal.TryParse(ABox.Text, A)
Decimal.TryParse(BBox.Text, B)
ResultBox.Text = (A + B).ToString("N2")
End Sub
If for some stupid reason you really, really, really need all that If crap, you will want to use ElseIf to ensure that the default case doesn't run.
Private Sub AddBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddButton.Click
Dim A as Decimal
Dim B as Decimal
Decimal.TryParse(ABox.Text, A)
Decimal.TryParse(BBox.Text, B)
If A = 0 then
ResultBox.Text = BBox.Text
ElseIf B = 0 then
ResultBox.Text = ABox.Text
Else
Dim result as Decimal = A + B
ResultBox.Text = result.ToString("N2")
End If
End Sub
But it's a waste -- and what's worse, if the point is to add two numbers, it's incorrect. Someone could put "I Like Cheese" in the first box and "YAAAAAAAAY!!!" in the other, and the result would be "YAAAAAAAAY!!!". Clearly not what should happen.
Private Sub AddBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles AddButton.Click
Dim A as Decimal
Dim B as Decimal
Try
A = Decimal.Parse(ABox.Text)
B = Decimal.Parse(BBox.Text)
ResultBox.Text = (A + B).ToString("N2")
Catch ex as FormatException
MessageBox.Show("Exactly two numbers (one per box), please!")
End Try
End Sub
But i detest doing that for user input. You should expect users to be malicious and/or idiotic, and count on them doing everything they can to mess up your app. It'll save you lots of debugging later on. But if we assume the worst, is malformed input really an exceptional condition? I say no. (Others may disagree. But they're wrong. :) )
If you want to notify the user of an input error, Decimal.TryParse also returns a boolean (true/false) saying whether it succeeded. If it failed, you can show a message telling the user to quit being an idiot. (Slightly more diplomatically, if you want.)
Secondly...turn Option Strict on. Please. All this implicit casting gives me the willies. Note that that will probably add like a dozen errors to your list...but that's a good thing. One should be aware when they're trying to put a number in a string variable and so on, cause lots of magic happens there that (in my opinion) people need to be more aware of.
K, now, as for debugging...if you've followed my advice up to this point, you shouldn't really need to debug. The exceptions are gone, and the code's going to be more correct before you can even compile it. But if you still have to, one way to go step by step through the program is to set a breakpoint on either the Function line itself, or the first line that isn't a declaration. (Click the left margin next to the line. A red ball should appear in the margin about where you clicked. That lets you know there's a breakpoint there.) You can probably try to set a breakpoint on the declarations too, but i seem to remember that causing the breakpoint to be either on the next line that actually does something, or on the beginning of the function. I forget which, and don't have VS on this machine to check.)
When you run the program from VS, the program will start, and when it hits the breakpoint, VS will pop back into the foreground with a yellow arrow where the red ball is. That yellow arrow points at the next line to be executed. From there, you can use the debug toolbar (look in your toolbars; you should have extra buttons, blue i think, that look like play/stop/etc buttons) to step through the code. You should also see windows labeled 'Locals', 'Watches', and other windows you'd expect to see in a worthwhile debugger. :)
Just be aware that while the program's stopped at a breakpoint, or stepping through the code, the app will appear frozen. UI updates probably will not appear right away, and consequently, you won't be able to type anything in the input boxes or hit the button til you resume (hit the "play' button).

Using vbAccelarator Win32 Hook crashes application along with VB IDE

I'm working on VB6 project where I need to have keyboard short-cuts for Buttons on Toolbar Control. To accomplish this, I have used Win32 Hooks library from vbAccelerator. Here's my IWindowsHook_HookProc function, that I use to retrieve Key strokes & perform action based on pressed short-cut (Ctrl + N for New, Ctrl + O for Open and Ctrl + S for Save) , but the I don't know what's wrong with the code that crashes my application along with VB6 IDE. The function is currently incomplete as I just tried to identify Ctrl + N key combination to test this feature. Please help me out.... :-|
Private Function IWindowsHook_HookProc(ByVal eType As EHTHookTypeConstants, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long, bConsume As Boolean) As Long
If KeyboardlParam(lParam).KeyDown Then
Select Case True
Case Me.ActiveControl = Me
If wParam = vbKeyControl + vbKeyN Then
frmNewReport.show
bConsume = True
End If
End Select
End If
Using a hook in the IDE can lead to lots of crashes, heck using a hook without fully understanding what you are doing is going to lead to a lot of crashes period...
Mark is correct about the Timer with the show form, as the Hook function should return as fast possible (< 50 ms) or your will end up with deadlock (and a crashed app) very quickly. Never set a breakpoint inside the Hook procedure, or you will kill your IDE (maybe crash, maybe hung, maybe some wierd state were you can never leave a breakpoint and you can't stop debugging). If you have a ton of long running functions you want to run based on a keypress, then set up a stack of actions to perform in the timer. Using a hook library is very powerful, but with great power comes great crashes...
I have no experience with that hooks library, but my guess is that you should do very little in the HookProc procedure itself. You are being called directly from the Windows API, rather than via the VB6 runtime. I'm not surprised that showing forms crashes everything out as you describe. Was there any advice on the vbAccelerator site about what sort of code to put in HookProc? vbAccelerator is an excellent site by the way.
I suggest you just set a flag variable somewhere to indicate that frmNewReport should be shown. You should have a Timer running with a short tick interval, say 100 milliseconds, which checks the flag variable: if the flag is set, clear the flag and show the form.
I've found a solution to my own question, its still crash-prone if not handled carefully, but now my application actually responds to key combinations that I wanted, Ctrl + N, Ctrl + O, etc.
Following is my rectified code that works fine as far as I know. Do suggest if you find any bug in it that lead to crash my application.
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Private Property Get CtrlPressed() As Boolean
CtrlPressed = (GetAsyncKeyState(vbKeyControl) <> 0)
End Property
Private Function IWindowsHook_HookProc(ByVal eType As EHTHookTypeConstants, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long, bConsume As Boolean) As Long
If wParam = vbKeyN Then
If CtrlPressed Then
LoadFormNewReport 'Method that opens Child Form 'New Report'
End If
bConsume = True
ElseIf wParam = vbKeyS Then
If CtrlPressed Then
SaveNewReport 'Method that saves new Report
End If
bConsume = True
ElseIf wParam = vbKeyF5 Then
If Not CtrlPressed Then
frmSettings.Show 'This form needs to be displayed Modally but if tried so then crashes application along with VB IDE, other short-cuts work fine.
bConsume = True
End If
End If
End Function

How do you make a MsgBox with a "Do Not Ask This Again" or "Don't Ask Me Again" Checkbox in VB6?

I ask this partly because I want to know the best practice way of doing this, and partly because the top google result I got was a forum thread from 2002 in which the question wasn't even answered.
I inherited some VB6 code, and there are some MsgBox calls in said code, many of which display messages that end users would probably find very annoying after a short time (e.g. "Printing Complete," "Record Added," etc.)
I would like to add the standard user interface control of a checkbox on the MsgBox saying "Don't ask me this again", so that when checked and OK is clicked, a setting is saved that lets the program know, you know...to never ask that again. Pretty standard control, the idea is fairly self-explanatory.
What I would like to know is what is the best practice way of doing this in VB6. There is the obvious way of just making a new form for these type of msgboxen and replacing the old MsgBox call with a .Show on that form, but do the VB6 gurus on Stack Overflow have a better way?
Thanks in advance
As far as I know, there is no other way. You need to make your own message box form with the check box. Of course, you'll also need to modify the code to store and retrieve this setting (and acting appropriately based on the setting).
I have done this in my own application many times. One thing to think about.... suppose the user checks the box "don't show me this again". In my opinion, there should be a way to reset the setting. Since the message box form won't show again, I added this to the configuration form (for my app).
One thing you may want to consider is sub-classing the MSGBOX function. You could create a function within your app that has a similar parameter list, but with a couple extra. If the extra parameters are missing, simply call vba.MsgBox (to get the standard behaviour). If you pass in extra parameters, you could call your new form instead.
Well... You are not absolutely correct guys ;)
Starting from Win2000, there's SHMessageBoxCheck function that does the trick.
VB6 declaration:
Private Declare Function SHMessageBoxCheck Lib "shlwapi" Alias "#185" (ByVal hWnd As Long, ByVal lpszText As String, ByVal lpszTitle As String, ByVal dwType As VbMsgBoxStyle, ByVal iDefault As Long, ByVal lpszId As String) As Long
For everything else follow the link :)
If you provide such a functionality, it might require "turning-on" of showing messagebox.
i.e. user should have an option to see the msgbox again using some setting.
Instead of that, you could use statusbar to display notifications OR have a label with the notification messages and turn it off after a few seconds.
This was my down and dirty solution:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
myfile = Workbooks.Application.ActiveWorkbook.Path & "\noprompt.txt"
If Dir(myfile) <> "" Then Exit Sub
exportData = MsgBox("Export data?" & vbCrLf & "Select Cancel (or × top right) to prevent this prompt from displaying again.", vbYesNoCancel, "Close Workbook")
If exportData = vbYes Then
Call ExportValues 'a separate function...
ElseIf exportData = vbCancel Then
'create file noprompt.txt
Open myfile For Output As #1
Write #1, "Delete this file to restore prompt to Export Data when workbook is closed."
Close #1
Exit Sub
ElseIf exportData = vbNo Then
Exit Sub
End If
End Sub
Documentation for my app explains that deleting the file restores the prompt.

Resources