Converting String to a DateTime or TimeValue - vb.net

I have a problem trying to convert a String to a DateTime or TimeValue.
I made a little Windows Form to use as an example. Please take a look at this to see the Form:
As you can see there is a TextBox1.
Also a Timer1 with an interval of 1000 ms.
What I want is to be able to fill in a time in the textbox (like this format: 22:30:00) and when your desktop reaches this time, it will display a messagebox.
I tried several old posts and tutorials with similar problems but I can't seem to find the solution. Can anyone make a quick example using my screenshot perhaps?
This is the code someone else suggested.
Dim tsValue As TimeSpan = TimeSpan.Zero
If TimeSpan.TryParse(TextBox1.Text.Trim, tsValue) Then
If Date.Now.TimeOfDay = tsValue Then
MessageBox.Show(String.Format("Messagebox test {0}", tsValue.ToString))
End If
End If
But for some reason it does not work when I place it in my Timer.

Keep a variable with the target time (assuming it passes validation) and have your timer check if the target time matches the current time. Make sure your timer updates the date when the day changes (12:00 AM).
Private m_dteTime As DateTime
Private m_dteCurrentDate As Date = DateTime.Now.Date
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
'Validation
Try
Dim strHours As String = TextBox1.Text.Substring(0, 2)
Dim strMinutes As String = TextBox1.Text.Substring(3, 2)
Dim strSeconds As String = TextBox1.Text.Substring(6, 2)
If IsNumeric(strHours) AndAlso IsNumeric(strMinutes) And IsNumeric(strSeconds) Then
m_dteTime = DateTime.Now.Date.AddHours(Convert.ToDouble(strHours)).AddMinutes(strMinutes).AddSeconds(strSeconds)
Else
m_dteTime = DateTime.MinValue
End If
Catch ex As Exception
End Try
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If DateTime.Now.Date > m_dteCurrentDate Then
m_dteCurrentDate = DateTime.Now.Date
End If
If m_dteTime.Date = DateTime.Now.Date AndAlso m_dteTime.Hour = DateTime.Now.Hour AndAlso _
m_dteTime.Minute = DateTime.Now.Minute AndAlso m_dteTime.Second = DateTime.Now.Second Then
MsgBox("The time is now.")
End If
End Sub

The reason your code is not working is you're using "=" operator to compare two values which are highly unlikely to be equal. The thing is, you're entering the time value without milliseconds in your TextBox control, but the TimeOfDay function returns current time including milliseconds. Therefore, your message box never gets shown.
Now, there are multiple ways to get around this, but let's keep it simple. If you change "=" to "<=", it will work, but in that case your message box will continue to appear with each timer iteration, so you would have to disable timer right after or just before you show a message box. Depending on what are you trying to achieve, this could or could not be the solution for you.
If you need to keep the timer running, you can remove the millisecond part of the current time before comparing, which will give you one second to do the comparing. Also, in this case you should reduce timer interval to a lower value (500ms should be enough).
Also, if I may suggest, instead of TextBox, you should use DateTimePicker control with Format property set to Time and ShowUpDown property set to True. It will make your life easier as it will automate validation and also avoid uneeded conversions.

Related

Want to display the processing progress using a Label to show a counter of sorts

Its a label printing app. Desire to show progress via a counter displaying in a Label. ie. would like it to
look like this... 1000 increments to 1001 increments to 1002, 1003 etc. Pretty simple pgm so obviously processing from start to finish is instantaneous. Thus end number pops up right away rather than flashing a sequence of sorts. Don't need number to be readable but just simulating the process. Since it is a printing program, there is time for this display. At the end of processing the final number should be on the screen. Thought maybe I could use a timer to tick off before updating label. My coding is obviously not correct. Here it is. Any suggestions are appreciated. I am a 79 YO tinkerer programmer (worked back in the COBOL days) so be kind LOL.
Private Sub Button2_Click_1(sender As Object, e As EventArgs) Handles Button2.Click
' Print button
Dim toPrint As Integer = Me.NumericUpDown2.Value
Dim RetVal As Object
Dim TopPos As String = "375,200"
Dim InfoPos As String = "360,260"
startNo = TextBox2.Text
For index As Integer = 1 To toPrint
RetVal = RDP.PrintRawData("^XA^LL450^PQ" & arrCust(4) & "^CFB,30^FO" & TopPos & "^FD" & arrCust(5) & StartNo & "^FS^FO" & InfoPos & "^FD" & arrCust(2) & "^FS^XZ")
Timer1.Interval = 1000
Timer1.Start()
startNo += 1
Next
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Label4.Text = startNo
End Sub
The problem is the loop never yields control back to the underlying windows event message pump, and so there's never a chance to process any paint messages to redraw the label until after everything is finished.
To fix this we first need to resolve two conflicting statements:
from start to finish is instantaneous
and
Since it is a printing program, there is time for this display.
Only one of those two statements can be true. They are mutually exclusive.
If the first statement is true, I'd change the timer to tick more often (every 200 milliseconds or so; faster is just wasteful) and put up with an ugly Application.DoEvents call in the loop so the label can have a chance to repaint. (Also: start the timer once, before entering the loop, and not on each iteration).
If the latter statement is true, I'll echo the usual warnings about avoiding Application.DoEvents (there are a lot of "gotchas" with this method). Instead, I'd do it the right way, and use a BackgroundWorker component.

How can I speed up VB copy

I'm running the following loop successfully when the number of items is low. However, when run against a larger list on the ListView, it seems to be taking way too long. I tested it with a list of 8,700 files and it took about two hours to complete. Is there something I can do to speed this up? I guess that removing the check for the Cancel button would help but I would like to keep that there for usability. As I've mentioned in earlier posts, I'm pretty new to Visual Basic so please provide lots of explanation with your suggestions. Thanks. Here's the code:
For i As Integer = 0 To m_CountTo
' Has the background worker be told to stop?
If BackgroundWorker1.CancellationPending Then
' Set Cancel to True
e.Cancel = True
Exit For
End If
'Select the row from the LVFiles ListView, then move the first column (0) into strSourceFilePath and the last
' column (3) into strDestFilePath. Execute the CopyFile method to copy the file.
LVFiles.Items(i).Selected = True
strSourceFilePath = LVFiles.SelectedItems(i).SubItems(0).Text
strDestFilePath = LVFiles.SelectedItems(i).SubItems(3).Text
My.Computer.FileSystem.CopyFile(strSourceFilePath, strDestFilePath, overwrite:=False)
' Report The progress of the Background Worker.
BackgroundWorker1.ReportProgress(CInt((i / m_CountTo) * 100))
' Me.LabelStatus.Text = FormatPercent((i + 1) / (intLVIndex + 1), 2) ' Show Percentage in Label
SetLabelText_ThreadSafe(Me.LabelStatus, FormatPercent(i / m_CountTo, 2))
Next
The Backgroundworker encapsulates a new thread. You cannot directly access controls that are created in another thread. If you do you will get an InvalidOperationException because of a cross-thread operation. The Backgroundworker however offers some functionality to share data (or access to controls) between threads. You should use them.
Private Sub StartBGW_Click(sender As Object, e As EventArgs) Handles StartBGW.Click
Dim dict As New Dictionary(Of String, String)
For i As Integer = 0 To m_CountTo
dict.Add(Me.LVFiles.Items(i).SubItems(0).Text,
Me.LVFiles.Items(i).SubItems(3).Text)
Next
Me.BackgroundWorker1.RunWorkerAsync(dict)
End Sub
First we prepare a dictionary that contains the source as Key and the target as Value. This object is given to the BackgroundWorker as a parameter.
Now comes the essential part:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim counter As Integer = -1
Dim dict = DirectCast(e.Argument, Dictionary(Of String, String))
For Each kvp In dict
counter += 1
' Has the background worker be told to stop?
If Me.BackgroundWorker1.CancellationPending Then
' Set Cancel to True
e.Cancel = True
Exit For
End If
'Select the row from the LVFiles ListView, then move the first column (0) into strSourceFilePath and the last
' column (3) into strDestFilePath. Execute the CopyFile method to copy the file.
My.Computer.FileSystem.CopyFile(kvp.Key, kvp.Value, overwrite:=False)
' Report The progress of the Background Worker.
Me.BackgroundWorker1.ReportProgress(CInt((counter / m_CountTo) * 100), counter)
Next
End Sub
We don't access the ListView anymore. Instead we use the dictionary that is given to us as a parameter through e.Argument. Theres also a slight difference in the BackgroundWorker1.ReportsProgress line. There's a second parameter I have used to pass the current index to the ProgressChanged event which can be obtained via e.UserState.
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Me.LVFiles.Items(Convert.ToInt32(e.UserState)).Selected = True
Me.LabelStatus.Text = e.ProgressPercentage.ToString
End Sub
This event is designed to be raised with a SynchronizationContext of the calling thread, in this case the UI thread. Here we can safely access any control and update them. The index is passed as e.UserState, so we can access the relevant item and set their Selected property to true.
The biggest improvement comes from the change of Me.LVFiles.SelectedItems(i).SubItems(0).Text to Me.LVFiles.Items(i).SubItems(0).Text. I'm not a professional, but it seems that SelectedItems isn't a real list. Instead it iterates through every item using the SendMessage API until the desired index is reached. This is why it takes longer the higher your index is. Everytime it starts with the first item and iterates through them. Lot of operations.
The second improvement is the separation of code that access UI controls. It's all done in one method now. More clear and readable.
Update: #Enigmativity mentioned that SelectedListViewItemCollection implements IList and therefore is a real list. Even though it has no underlying list containing all selected items like you have in ListViewItemCollection. My point was to say, that accessing a single element is more complicated.

Can I make a message box appear if a textbox time is 5 minutes before system time?

I am trying to create a reminder system for my break schedule (because for some reason the tech company I work for does not have one in our scheduling system /facepalm)
I can make it work IF the time I write in the textbox for my break matches the system time. What I would like it to do is change it so it pops up once, 5 minutes before and then again when it matches the system time. Here is what i have so far for the on time reminder:
Sub Timer1Tick(sender As Object, e As EventArgs)
DaClock.Text = Format(Now, "h:mm")
For the sake of simplicity ^
If Coffee.Text = "DaClock.Text" And reminder.Checked = True Then
Coffee.Text = (Coffee.Text + " Over")
Coffee.Enabled = False
MsgBox("Break Time", MsgBoxStyle.Information, "break")
Else If Coffee.Text = DaClock.Text Then
Coffee.Text = (Coffee.Text + " Over")
Coffee.Enabled = False
End If
End Sub
I find that I am not able to figure out how to get the reminder to pop up 5 minutes before.
edit Oh it may not be relevant but DaClock is an invisible Label
Update
I was able to set up a secont label and a string with 5 minutes added so the event will trigger 5 minutes early but now i am having formatting issues:
Dim MyTime As String
MyTime = TimeOfDay.AddMinutes(5)
D5Clock.Text = Format(MyTime, "h:mm")
But all it shows in the Label is h:mm. If i choose not to format it and shows normally, (eg: 6:30:54 PM) but the formatting is important to make sure my break entries trigger as we are only using the h:mm (eg 6:30) in the fields.
While I can respect what you're doing, and especially that you progressed and just straight out beaten the shit out of your own challenge, I would like to suggest an alternative (explanations follows):
Public NotInheritable Class Reminder
Private Shared _instance As Reminder
Private Sub New()
'hehehe Private
End Sub
Public Shared Function GetInstance() As Reminder
If _instance Is Nothing Then _instance = New Reminder
Return _instance
End Function
Private Async Sub RemindMe(ByVal time As DateTime)
Dim waiting As Boolean = True
While (waiting)
Await Task.Delay(60000) 'This means "Check once every minute"
If time > Now Then
MessageBox.Show("Wake up, it's " & Now.ToShortTimeString & " !!")
waiting = False
End If
End While
End Sub
Public Sub Dispose()
Me.Dispose()
End Sub
End Class
So... what exactly is this unholy thing? It's a Singleton. It's a class which has only one instance, at all time - that's why the Sub New() is private: I don't want people to be able to instantiate this class! Except for you. You can get ONE, only one instance, by using the public function GetInstance() (if you ask for more than one instance, you'll always get the first one, so ask away if you feel like it).
Then you can ask it to remind you to do stuff by giving it a time when to awaken. It keeps track of your reminders on different threads, so don't worry about these hogging all your main thread's cpu. This is just a skeleton code snippet, though, and I suggest you alter it with custom messages and the like.
Don't forget to Dispose() of it if you want to keep your memory happy. It'll die eventually anyway when you dispose of it's parent object, but it's a healthy habit nonetheless.
It may or may not be something which will help you, and don't mind me if it's not something you want to deal with, but I felt like you might like it. Have fun!
Figured it out by running two clocks simultaneously and calling each one so it will happen 5 minutes early and then again on time.
D5Clock.Text = Format(TimeOfDay.AddMinutes(5), "h:mm")
DaClock.Text = Format(TimeOfDay, "h:mm")
whew

Reading Numbers from Webbrowser Vb.net

In my WebBrowser I got text like this: Remaining balance: 10$
I would like to convert it to another currency, I want just to read number from my browser, then after that I will send it to a Label or TextBox with the new converted currency. I am stuck here.
A screenshot of it
Private Sub LinkLabel2_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabel2.LinkClicked
WebBrowser2.Navigate(TextBox9.Text + TextBox2.Text)
End Sub
Private Sub WebBrowser2_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser2.DocumentCompleted
Label1.Text = (WebBrowser2.Document.Body.InnerText)
End Sub
I have this suggestion (I know this is probably not the perfect solution):
First, try loading that same webpage in a normal web browser such as Google Chrome or Firefox or any browser which has the feature to "inspect elements", meaning to view their HTML code.
Find out the element which displays the price you want.
Note down the ID of the element (usually written something like id="someID")
Back to your program's code, include the following Function, which will get the text displayed and convert it to another currency:
Public Function ConvertDisplayedCurrency() As Decimal
Dim MyCurrencyElement As HtmlElement = WebBrowser2.Document.GetElementById("theIdYouGotInStep3") 'This will refer to the element you want.
Dim TheTextDisplayed As String = MyCurrencyElement.InnerText 'This will refer to the text that is displayed.
'Assuming that the text begins like "Remaining balance: ###", you need to strip off that first part, which is "Remaining balance: ".
Dim TheNumberDisplayed As String = TheTextDisplayed.Substring(19)
'The final variable TheNumberDisplayed will be resulting into a String like only the number.
Dim ParsedNumber As Decimal = 0 'A variable which will be used below.
Dim ParseSucceeded As Boolean = Decimal.TryParse(TheNumberDisplayed, ParsedNumber)
'The statement above will TRY converting the String TheNumberDisplayed to a Decimal.
'If it succeeds, the number will be set to the variable ParsedNumber and the variable
'ParseSucceeded will be True. If the conversion fails, the ParseSucceeded will be set
'to False.
If Not ParseSucceeded = True Then Return 0 : Exit Function 'This will return 0 and quit the Function if the parse was a failure.
'Now here comes your turn. Write your own statements to convert the number "ParsedNumber"
'to your new currency and finally write "Return MyFinalVariableName" in the end.
End Function
Call that Function probably when the document of the WebBrowser2 loads, that is, in the WebBrowser2_DocumentCompleted sub.
I hope it helps!

Label Text Changes During Code but Won't Say Changed

I have code in my program that updates a database. When the database has been updated, I change the text of a label to say "Last Update" and then the actual time when the update occurred. This part works perfectly.
My problem occurs when I close the program and re-open it. I want to code to check the date in the label and if the date in the label is less than the current date I want to update my database. But when I close my program and re-open it the label in the text doesn't stay.
Here is my code:
Public Sub Screen_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Dim Time_of_Update = CDate(Label_Time_of_Update.Text.Split(" "c)(2).Trim())
Debug.WriteLine(Time_of_Update)
If Time_of_Update < Today Then
Update_Data()
Else
End If
End Sub
Private Sub Update_Data()
Update_Daily()
Ready_Update_Quarterly_and_Annualy()
Dim Time_of_Update = DateTime.Now
Label_Time_of_Update.Text = "Last Updated " & Time_of_Update & ""
End Sub
How can I fix this?
Go to Settings in Project properties and create a LastUpdatedDate setting:
Name == "LastUpdatedDate" basically a variable name
Type == Date
Scope == User (Application scope makes them Read Only)
Value == a valid date
This defines the setting name and data type. in code:
Dim Time_of_Update = DateTime.Now
Label_Time_of_Update.Text = "Last Updated " & Time_of_Update & ""
My.Settings.LastUpdatedDate = Time_of_Update
My.Settings.Save
next time you run:
Label_Time_of_Update.Text = My.Settings.LastUpdatedDate
Saving it as a DateTime type will make it easy to date comparison in code rather than converting it back from a String.