Looping Until A File Exists - vb.net

Ok, the following codes shows how I am enterting a value into a textbox, adding that value to the listbox, updating a picturebox next to it and blanking out the textbox so the user can add additional values to the listbox.
ListBox1.Items.Add(TextBoxTicketID.Text)
If CStr(ListBox1.Items(0)) = TextBoxTicketID.Text Then
PictureBoxStatus1.Image = My.Resources.Orange_Information
End If
TextBoxTicketID.Text = ""
I have another process not shown here that will create a PDF based on the value that was entered into the listbox.
I'm having trouble with a loop to check a specific directory if the PDF exists or not. When the PDF exists, I'll change the picturebox to another image.
Here is the loop that I was using, but the issue I ran into was that the user couldn't enter a second value unless the first value was present.
Loop Until My.Computer.FileSystem.FileExists("c:\Temp\" + ListBox1.Items(0) + ".pdf")
PictureBoxStatus1.Image = My.Resources.Green_Checkmark
So in theory, I need to be able to enter X amount of values into the listbox and keep checking to see if the file exists and if it does, change those images that needed.
EDIT
Here's what I ended up doing...seems to be working fine though...
ListBox1.Items.Add(TextBoxTicketID.Text)
If CStr(ListBox1.Items(0)) = TextBoxTicketID.Text Then
PictureBoxStatus1.Image = My.Resources.Orange_Information
End If
TextBoxTicketID.Text = ""
Call CheckFiles()
Added a public sub
Public Sub CheckSpooling()
Dim Watcher As New FileSystemWatcher()
Watcher.Path = "C:\Temp\"
Watcher.Filter = ListBox1.Items(0) + ".pdf"
AddHandler Watcher.Created, AddressOf OnChanged
Watcher.EnableRaisingEvents = True
End Sub
Then the sub to run whatever is needed if the file was added. I used a msgbox for testing.
Private Shared Sub OnChanged(source As Object, e As FileSystemEventArgs)
' Specify what is done when a file is created.
MsgBox("File has been created!")
End Sub

Check out the FileSystemWatcher

The reason the user can't enter anything while you are looping is because the WinForm framework is essentially single threaded. Everything in the UI occurs on the same thread, including the event handler. So, if you are sitting in a loop for a long time in a button click event handler, then the UI will be locked up and unresponsive until the code exits the loop. The way to get around this is to start a new thread to perform whatever work needs to be done. That worker thread can take as long as it needs to complete and it won't interfere with the UI thread so the UI remains responsive. This is made easier by the BackgroundWorker component which you can drop onto your forms in the form designer.
However, the FileSystemWatcher, as Dan-o has recommended is probably a better solution than creating your own worker thread that keeps checking if the file exists. Not only does it avoid re-inventing the wheel, but it also will be more efficient. Instead of constantly asking the file system if a file exists, it just listens to messages from the file system to find out when changes occur.

Related

Label Text not updating even when using Refresh, Invalidate, and DoEvents

My code is designed to be a control system for a 2-axis motion system. I have 2 drives that each output a count of their steps. I can read the device, update a property, and update the text field of a label. However, it does not update the form. When I use a message box, I can display the text value being correct, but nothing updates the label.
I'm happy to try any suggestions, but I've been fighting this for about 16 hours and I'm at my wits end - as evidenced by the clear overkill/terrible coding that is shown in the code. I can't understand why it's not updating.
Additionally, a manual button with all versions seen below to refresh a form doesn't update the control.
Direction, recommendations?
Private Sub PositionChanged(ByVal sender As Object, ByVal e As EventArgs)
If TraverseController.InvokeRequired Then
TraverseController.Invoke(
New EventHandler(Of EventArgs)(AddressOf PositionChanged), sender, e)
Return
End If
'RaiseEvent PropertyChanged(TraverseController, New System.ComponentModel.PropertyChangedEventArgs("Position"))
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
TraverseController.lblLinearDrivePosDisp.Text = CStr(_position)
Application.DoEvents()
TraverseController.lblLinearDrivePosDisp.ResetBindings()
TraverseController.GBDrivePositionDisp.Refresh()
TraverseController.lblLinearDrivePosDisp.Refresh()
TraverseController.Refresh()
TraverseController.Invalidate()
TraverseController.Update()
Application.DoEvents()
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
End Sub
Assumption: TraverseController is form's class name.
This looks like a VB default form instance issue. It is apparent that you are trying to properly marshal control interaction back to the UI thread by using checking TraverseController.InvokeRequired. However, due to the way these default instance are created, TraverseController.InvokeRequired is creating a new instance of TraverseController on the secondary thread and all subsequent code is modifying that instance and not the one created on the UI thread.
One way to deal with this is to pass a synchronizing control instance to the class where PositionChanged changed method is defined and check that control's InvokeRequired method instead of TraverseController.InvokeRequired. If the containing class is itself a UI control, then use that class instance (Me.InvokeRequired).

How to set a listview FocusedItem programatically

How can I set the FocusedItem property programatically?
I've tried this so far with no success:
If lvw.FocusedItem Is Nothing Then
If lvw.Items.Count > 0 Then
lvw.Focus()
lvw.HideSelection = False
lvw.Items(0).Selected = True
lvw.Items(0).Focused = True
lvw.FocusedItem = lvw.Items(0)
lvw.Select()
End If
End If
btw the form where the listview is has not called the ShowDialog method yet.
Can this be the reason for this not to work?
You have to understand that each control on the Form and the Form itself is a window and for the window to have focus it must first be created and assigned a handle.
For a basic description of this, I refer you to: All About Handles in Windows Forms The following excerpts are from the referenced article.
What is a Handle?
A handle (HWND) is the return value from CreateWindowEx which the Windows Operating System uses to identify a window. A "window" in win32 is a much broader concept than you may think - each individual button, combobox, listbox etc comprises a window. (For more information see About Window Classes ) NOTE: there are other things known as "Handles" in the Framework - e.g. GDI Handles from a Bitmap or Handles to Device Contexts (HDCs) - this article discusses HWNDs only.
...
When does a Control create its handle? (When does a control call CreateWindowEx?)
A control tries as much as possible to defer creating its handle. This is because setting properties forces chatty interop between the CLR and user32.
Typically the handles for all the controls are created before the
Form.Load event is called. Handles can also be created if the "Handle"
property is called and the handle has not yet been created, or
CreateControl() is called.
So a window's handle is not immediately created when you instantiate a control. However, you can force a control to create its handle by referencing its Handle property.
So if you first get the ListView to create its handle, then when you set those properties that you wanted.
Dim f2 As New Form2
' you do not need this condition, it is here only for demonstration purposes
' so that you can step through the code in the debugger and observe the
' code execution.
If Not f2.ListView1.IsHandleCreated Then
' retrieval of the Handle will cause a handle to be created
' if it has not yet been created
' if you delete the If-Then block, you will need to retain the
' following statement
Dim h As IntPtr = f2.ListView1.Handle
End If
f2.ListView1.FocusedItem = f2.ListView1.Items(2)
f2.ListView1.Items(2).Selected = True
f2.ListView1.Items(2).Focused = True
f2.ActiveControl = f2.ListView1
f2.ShowDialog()
As others have commented, your code should work as written. If all you need is to programmatically access the focused item in your code, you shouldn't be experiencing any difficulties. (If you are, please describe them.)
If you are looking for a visual effect (the row being highlighted), my guess is that your code is in another control's event and the focus is being set back to that control automatically the instant after your code runs. More than likely your code needs to be where it is and trying to move it elsewhere to prevent this issue would be a waste of time.
However, there are other ways to set a row apart visually. When a list view isn't likely to stay focused, my preferred method is to distinguish the selected item with a different fore/back color. (You can use the focused item if you prefer, but I find the selected item more useful. Your call.)
Here is an example which visually highlights the selected row, regardless of focus:
Private Sub lvw_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lvw.SelectedIndexChanged
If lvw.Items Is Nothing Then Exit Sub
For Each lvi As ListViewItem In lvw.Items
If lvi.Selected = True Then
lvi.ForeColor = Color.DarkGray
lvi.BackColor = Color.LightCyan
Else
lvi.ForeColor = Color.Black
lvi.BackColor = Color.White
End If
Next
End Sub
EDIT:
In response to the added information that this form is being displayed using ShowDialog, yes, that is likely the source of your problem.
ShowDialog creates a new instance of the form. Therefore, if you have set any properties of a form or its controls, and later call ShowDialog to display that form, the form displayed is a new copy of the original form and will not reflect the changes you made programatically.
Imagine you sit down at a computer where a blank Word document is already open. You type something in it and then open a new document. The text you typed in the first document is not copied to the second. I think this is the root of your troubles here.

vb.net - redirect output to form textbox

I am trying to read/use the output from a python program in my vb.net project so far I'm not getting any results. What I'd like to see is the python program run (just by itself first) and all of the output get redirected into a textbox.
I've looked at some other posts about this, but I'm either missing something or not understanding something, as all I'm getting is blank output.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim PythonPath = "C:\Python27\"
Dim strPath As String = Application.StartupPath
MessageBox.Show(PythonPath & "python.exe """ & strPath & "\Resources\import_logs.py"" ")
Dim start_info As New ProcessStartInfo(TextBox1.Text)
' Make the process and set its start information.
Dim process As New Process()
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
process.StartInfo.FileName = PythonPath & "\python.exe"
process.StartInfo.Arguments = """" & strPath & "\resources\import_logs.py"""""
process.StartInfo.UseShellExecute = False
process.StartInfo.CreateNoWindow = True
process.StartInfo.RedirectStandardOutput = True
'process.StartInfo.RedirectStandardError = True
AddHandler process.OutputDataReceived, AddressOf proccess_OutputDataReceived
process.Start()
process.BeginOutputReadLine()
End Sub
Public Sub proccess_OutputDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
On Error Resume Next
' output will be in string e.Data
' modify TextBox.Text here
'Server_Logs.Text = e.Data ` Does not display anything in textbox
MsgBox(e.Data) 'It works but I want output in text box field
End Sub
End Class
Eventually I'm going to pass arguments to the python script and I'd like to get feedback that I can then use (insert error into a database, email when it's done, etc), so I'd like it to capture the process while running and not just a data dump at the end.
Any help would be much appreciated.
First things first—it's no wonder you aren't sure what's wrong with your code, you're silencing all errors that could possibly help you to diagnose it. That's the only purpose of On Error Resume Next in VB.NET. That unstructured error handling was included only for backwards compatibility with the pre-.NET versions of VB and it's time to forget that it ever existed. You certainly don't want to use it in code. (I would say "in code that you're debugging", but all code is a potential candidate for debugging and ignoring errors is just dumb.)
Anyway, on to the specific problem. We know that the call to MsgBox works, but it doesn't work right when you start interacting with controls on your form. So something is falling apart there.
It turns out that the OutputDataReceived event is raised on an entirely different thread, a different one than was used to create the process and a different one than is running your application's UI. It actually just retrieves a thread from the system thread pool.
And that's where the problem lies: you cannot manipulate UI objects on a thread other than the one that created those objects (at least not without jumping through some hoops), which is precisely what your code tries to do here. In fact, you're probably swallowing an exception that would have rather obtusely informed you of this situation.
The simple fix is to set the SynchronizingObject property of the Process class to one of your UI components (like the form, or the specific control you want to output to). This forces all event handlers to execute on the same thread that created that component. At that point, your code should work fine, because you're not trying to do any cross-thread UI access. (Message boxes are not vulnerable to this because any thread can display a message box. You're not trying to access an existing UI object that is bound to another thread.)
Alternatively, you could handle the marshalling yourself in the event handler method through the use of delegates and the BeginInvoke method, but this seems like unnecessary work to me.

Visual Basic Console Application Download Percentage?

I am using DownloadFileAsync to download a larger file (1.3 GB), but i'd like to add a simple percentage indicator (ex. 64%). I'm new to Visual Basic I have no idea how to do this.
Any help would be appreciated.
The WebClient class has a DownloadProgressChanged event that you can listen to if you want to update a progresss display. For instance, if you’ve got a console application, it’s as simple as:
Dim client As New WebClient()
AddHandler client.DownloadProgressChanged, AddressOf ProgressUpdate
client.DownloadFileAsync(yourURI, yourFile)
Sub ProgressUpdate(sender As Object, e As DownloadProgressChangedEventArgs)
' Reset cursor position …
Console.CursorTop -= 1
Console.CursorLeft = 0
Console.WriteLine("{0}% completed", e.ProgressPercentage)
End Sub
If, on the other hand, you are on a Form in a WinForms project and you’ve got a label ProgressLabel that you want to update, the following code will do that:
Sub ProgressUpdate(sender As Object, e As DownloadProgressChangedEventArgs)
Dim s = String.Format("{0}% completed", e.ProgressPercentage)
Me.Invoke(New Action(Sub()
ProgressLabel.Text = s
End Sub))
End Sub
The ProgressUpdate method is a bit complicated due to multithreading:
The WebClient is running the asynchronous file download in a background thread. However, form controls can only be updated from the foreground thread that the form is running in. For that reason, we cannot update the label directly inside the ProgressUpdate event (because that, too, is being invoked, and running, in the background thread1).
So what we do instead is use the Form.Invoke method which guarantees that whatever we want to execute is execute in the form’s own thread. We pass an Action delegate to the Invoke method which contains the code that we want to execute. And that code is just updating the label.
1 At least I couldn’t find anything in the documentation saying otherwise – the event might actually execute in the foreground thread but in that case the above code still works.

VB.Net Winform Application based on BackgroundWorker Thread concept - UI update issue

Team,
I have build a VB.Net windows application which does uploads data into database and basically updates two controls:
1. A textbox which is constantly updated with one line per database record upload.
2. A label which keeps track of the count of database record uploaded.
I have used BackgroundWorker thread concept, where the thread's bgwWorker_DoWork() method contains the business logic for upload and bgwWorker_ProgressChanged() updates the 2 UI controls based on uploads.
But the issue I am facing is that I do not get complete updates on both the UI controls. Sometimes the thread bypasses update of textbox and sometimes of label. I could resolve this issue by adding System.Threading.Thread.Sleep(25) before each UI control update code. Is this correct way of solving the issue? OR is there something I am missing?
Kindly suggest.
Below is the code in both these methods:
Private Sub bgwWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwWorker.DoWork
.................
.................
'Updates database record related update in textbox
System.Threading.Thread.Sleep(25)
updater.eventName = "UpdateStatusBox"
updater.errorMessageToLog = String.Empty
updater.errorMessageToLog += GetErrorMessage(dataTable(rowNumber)("Name").ToString(), ExceptionData)
bgwWorker.ReportProgress(1, updater)
.................
.................
'Updates Status Count in LABEL
System.Threading.Thread.Sleep(25)
updater.eventName = "UpdateStatusBar"
updater.successCount = successCount.ToString()
updater.failureCount = failureCount.ToString()
bgwWorker.ReportProgress(2, updater)
End Sub
Private Sub bgwWorker_ProgressChanged(ByVal sender As System.Object, ByVal e As ProgressChangedEventArgs) Handles bgwWorker.ProgressChanged
Dim updater As UIUpdater = TryCast(e.UserState, UIUpdater)
..........................................
If updater.eventName = "UpdateStatusBar" Then
UpdateStatusBar(updater.successCount, updater.failureCount)
ElseIf updater.eventName = "UpdateStatusBox" Then
txtUpdates.Text = txtUpdates.Text & updater.errorMessageToLog
End If
.....................................
End Sub
I'm almost positive that your problem is your instance of the UIUpdater object called updater. This object appears to be declared globally and is thus shared between calls.
Omitting a little bit of code this is what you have:
updater.eventName = "UpdateStatusBox"
bgwWorker.ReportProgress(1, updater)
updater.eventName = "UpdateStatusBar"
bgwWorker.ReportProgress(2, updater)
Although you call ReportProgress() linearly, it doesn't fire your ProgressChanged event immediately nor does it block until that method completed. To do so would defeat the purpose of threading if you think about it.
To put it another way, you have a global object that you are setting a property on. You then say "when someone gets a chance, do something with this". You then change a property on that global object and sometimes this happens before "someone has done something" happens.
The solution is either to create two global variables, one for each possible event or to just create an instance variable when needed. I'm not sure that its thread safe to use a global variable the way you are so I would recommend just creating an instance variable. In fact, the state object you pass to ReportProgress could just be a string.
I would NOT use a sleep in your DoWork event.
Have you tried refreshing the control after you update it? Each control has a Refresh method which forces a redraw. This may result in flickering though.
Another option is to include the information needed for both controls (textbox and label) in a single call to ReportProgress rather than trying to make two calls.