I have written in vb.net for progress bar. I am thinking of there is a better way than this.
Here is what my code is :
Private Function ImportDataFiles(ByRef pobjDataLoadDTO As DataLoadDTO) As Boolean
Try
lblStatus.Visible = True
lblStatus.Text = ""
myProgressBar.Visible = True
myProgressBar.Value = 0
For Each drRow As ImportData.TRow In pobjDataLoadDTO.FileInfo.Select("categ_code = 'abc'")
If pobjDataLoadDTO.FileTimes.ContainsKey(drRow.KEY_CODE) AndAlso _
pobjDataLoadDTO.FileTimes(drRow.KEY_CODE) > pobjDataLoadDTO.UploadTimes(drRow.KEY_CODE) Then
pobjDataLoadDTO.DestinationTablename = drRow.KEY_CODE
If mobjDataLoadBO.ImportDataFiles(pobjDataLoadDTO) Then
drRow.DATA_TXT = mobjCommonBO.ONow.ToString
End If
End If
lblStatus.Text = drRow.KEY_CODE.Trim & "is loading...."
lblStatus.Refresh()
myProgressBar.PerformStep()
lblStatus.Refresh()
Next
Return True
Catch ex As Exception
Return False
End Try
End Function
Right now It is working, But I want to use more efficient way, like using Backgroundworkerprocess...etc., Any Ideas on this one?
Since your function runs in main thread, I assume your application is freezing and not very smooth while upload in progress.
1 - Drop Backgroundworker control on the form
2 - set "reportProgress" property of the worker to "True"
3 - Move your loop code into "DoWork" event of the worker control. And call worker.RunWorkerAsync. You can pass needed arguments to it
4- the code that refreshes progress bar move into "ProgressChange" event of the worker. This is important as you can't call control from worker thread. and ProgressChange is running in the main thread. You can also delete "Refresh" method call. That will not be needed anymore. Every time you want to refresh the progress bar call "ReportProgress" method of the worker
5-Use "RunWorkerCompleted" worker event, to do your clean up, and hide your progress bar
Its also might be a good idea to check if worker is already working before initiating, like
If worker.IsBusy Then
If worker.CancellationPending = False Then
worker.CancelAsync()
End If
Exit Sub
End If
Related
I have a VB.NET 4.6.1 desktop app that has been using FindWindow and FindWindowEx for over 2 years with no issue to locate a MDI child window and capture the window caption text, it has worked flawlessly until recent.
The behavior now is my app can only successfully obtain the MDI client window handle if I go back to either the parent window or MDI client and click anywhere on either window, then return to my app and the process succeeds.
I have tried adding threading sleep events, running the action continuously in a loop multiple times, calling AppActivate method using process ID (thinking I just needed to execute again), my next workaround thought is to try and send a click event to the parent window prior to my action being executed or maybe to use Enumerate all child windows of the parent, hope someone can suggest something because I am at a roadblock, been doing this for years but this one doesn't make sense to me, I have the suspicion that it is related to recent ownership of the software company and them revising this section, but I have no idea why it would interfere with these root level API methods.
Sample Code:
MDIhWnd = FindWindowEx(ParenthWnd, IntPtr.Zero, "WindowsForms10.MDICLIENT.app.0.34f5582_r7_ad1", Nothing)
'Threading.Thread.Sleep(100)
'AppActivate(proc(0).Id)
If MDIhWnd = 0 Then
Threading.Thread.Sleep(100)
'Dim hw = GetTopWindow(ParenthWnd)
For i = 0 To 500
AppActivate(proc(0).Id)
MDIhWnd = FindWindowEx(ParenthWnd, IntPtr.Zero, "WindowsForms10.MDICLIENT.app.0.34f5582_r7_ad1", Nothing)
If MDIhWnd <> 0 Then
Exit For
End If
Next
End If
The solution for me was, based on the above suggestion, to use UI Automation, I
had never worked with it before, however after looking it over I gave a go and
found that it did indeed simplify my needs to capture window text from a 3rd party application window with MDI Client Interface.
Below is a lessor version in VB.NET of the process for anyone needing to do the
same thing:
Imports System.Windows.Automation
' You will also need references to UIAutomationClient, and UIAutomationTypes
Private Sub test_ui_automation()
Dim ParenthWnd As Integer = 0
Dim _AutomationElementA As System.Windows.Automation.AutomationElement = Nothing
Dim _AutomationElementB As System.Windows.Automation.AutomationElement = Nothing
Dim _AutomationElementC As System.Windows.Automation.AutomationElement = Nothing
Dim propCondition As Condition
Try
'Parent Windows Process Stuff
ParenthWnd = FindWindow(Nothing, "Application to Find")
_AutomationElementA = AutomationElement.FromHandle(ParenthWnd)
If _AutomationElementA Is Nothing Then
NotifyIcon1.BalloonTipIcon = ToolTipIcon.Error
NotifyIcon1.BalloonTipText = "Couldn't Locate Parent Window."
NotifyIcon1.Visible = True
NotifyIcon1.ShowBalloonTip(3000)
Exit Sub
End If
' MDI Client Stuff
' I used ClassNameProperty but other conditions are available
propCondition = New PropertyCondition(AutomationElement.ClassNameProperty, "WindowsForms10.MDICLIENT.app.0.34f5582_r7_ad1", PropertyConditionFlags.IgnoreCase)
_AutomationElementB = _AutomationElementA.FindFirst(TreeScope.Element Or TreeScope.Children, propCondition)
If _AutomationElementB Is Nothing Then
NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning
NotifyIcon1.BalloonTipText = "Application warning MDIClient not Available!"
NotifyIcon1.Visible = True
NotifyIcon1.ShowBalloonTip(3000)
Exit Sub
End If
' Final Stage Stuff Locate Window Containing Class with Caption
propCondition = New PropertyCondition(AutomationElement.ClassNameProperty, "WindowsForms10.Window.8.app.0.34f5582_r7_ad1", PropertyConditionFlags.IgnoreCase)
_AutomationElementC = _AutomationElementB.FindFirst(TreeScope.Element Or TreeScope.Children, propCondition)
If _AutomationElementC Is Nothing Then
NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning
NotifyIcon1.BalloonTipText = "Automation warning, MDI Details are open."
NotifyIcon1.Visible = True
NotifyIcon1.ShowBalloonTip(3000)
Exit Sub
End If
Caption = _AutomationElementC.Current.Name
' If needed you can now parse/strip any data needed from the Caption text.
' I had other processes here but could not include in the post.
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
This is my code:
For i As Integer = 0 to 20
mythread = New System.Threading.Thread(AddressOf myfunction)
mythread.Start()
Next
I want to kill all threads that are running myfunction()
I tried:
For Each hello123 As System.Threading.Thread(AddressOf myfunction)
thread.abort(hello123)
Next
But came up with an error (on AddressOf):
Array bounds cannot appear in type specifiers.
To stop a function, we need to define a boolean. Then, function needs to be inside do while our_boolean = true loop. Stop button's code must have our_boolean = false, so when it's pressed, do while loop is gonna end immediately.
It looks something like this:
Do While potato = True
'code
Loop
When stop button is pressed, loop is gonna stop immediately because potato is set to false. Then when we want to continue running the code, we just need to set potato to true and call function.
Is there some way to have a 'listener' of sorts to listen for a button click in the middle of code? There are certain scenarios where I won't have to wait for the code to complete before I can exit the functions but I cannot seem to find a way to see if the button was clicked other than throwing a ton of if checks throughout the function calls which doesn't seem very efficient to me.
This code runs on the thread, different from your main application
private function LongRunningFunction()
Din canceled as boolean = false ' to make sure it is canceled while in the loop
' this is very long loop
For i as integer = 0 to 100000
If _cancelExecution Then
canceled = true
Exit For
End If
' your code runs here
.........
Next
If canceled Then
' Wrap up this thread, clean up stuff, etc
End If
.......................
End Function
On the main thread, when button is clicked _cancelExecution is set to true
The BackGroundWorker has this mechanism built-in already
I've run into a little issue with a simple file copying application I'm writing in Visual Basic 2005. I have a main thread which looks after the GUI, and for the file scanning/copying I've created a separate thread which I create like this:
trd_copy = New Thread(AddressOf CopyTask)
trd_copy.IsBackground = True
trd_copy.Start()
This works fine for the scanning phase of the operation, and I can use the buttons in the GUI just fine. The problem is that when CopyTask gets to the file copying phase (using File.Copy), the main thread appears to lock up, and the GUI with it, meaning that the button I have there for aborting the copy operation is useless. When the copying is done, all returns to normal, and during copying the sub thread can update the status bar on the main form.
I'm sure I'm missing something simple, but I can't for the life of me see what it is.
Thanks very much!
Edit: Adding the code for CopyTask():
Private Sub CopyTask()
Control.CheckForIllegalCrossThreadCalls = False
If check_scanfirst.Checked Then
status1.Text = "Scanning..."
bytestocopy = 0
scandir(src)
filesscanned = True
MsgBox("Scanning completed")
End If
If check_delete.Checked Then
' Do a clean of the destination, removing any files that don't exist in the source dir
End If
If filesscanned Then
ProgressBar1.Visible = True
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = 100
ProgressBar1.Refresh()
End If
checkdir(src)
MsgBox("Copying completed")
If filesfailed > 0 Then
MsgBox("Warning: " + Str(filesfailed) + " files were not copied successfully.")
End If
guistop()
End Sub
From what i can tell all calls to the System are also GUI Thread based no matter if you want them or not. And Control.CheckForIllegalCrossThreadCalls = False is very bad, you always want to write your thread and ( if beginner ) run code and everytime it breaks out to code to write a delegate and an invoke function for that part of your thread ( taking up the most minimal time ) one can in the Gui(Main) thread.
Here is an example with full sourcecode
http://www.codeproject.com/Articles/15104/Multithreading-with-VB-NET-A-beginner-s-choice
in your thread
Control.CheckForIllegalCrossThreadCalls = False -- this needs removed
If check_scanfirst.Checked Then -- this needs a delegate and invoke method
status1.Text = "Scanning..." -- this needs a delegate and invoke method
bytestocopy = 0
scandir(src) -- if scandir(has calls to gui )this needs a delegate and invoke method but inside of Scandir()
filesscanned = True
MsgBox("Scanning completed") -- this needs a delegate and invoke method ( keep in mind thread will keep running even if mesgbox not clicked )
End If
If check_delete.Checked Then -- this needs a delegate and invoke method
' Do a clean of the destination, removing any files that don't exist in the source dir
End If
If filesscanned Then
ProgressBar1.Visible = True -- this needs a delegate and invoke method
ProgressBar1.Minimum = 0 -- this needs a delegate and invoke method
ProgressBar1.Maximum = 100 -- this needs a delegate and invoke method
ProgressBar1.Refresh() -- this needs a delegate and invoke method
End If
checkdir(src)
MsgBox("Copying completed") -- this needs a delegate and invoke method
If filesfailed > 0 Then
MsgBox("Warning: " + Str(filesfailed) + " files were not copied successfully.") -- this needs a delegate and invoke method
End If
guistop() -- this needs a delegate and invoke method if (guistop makes calls to gui )
so if you can't tell your making lots of calls to the gui... heres how i would write it to make it really simple
how i would write your code ( wrote this in word some code may need a tweak )
Private sub DoStuffBeforeCopy()
If check_scanfirst.Checked Then
status1.Text = "Scanning..."
bytestocopy = 0
trd_copy.ParameterizedStart(src) //start thread
end sub
CopyTask(byval src as *string*?)
scandir(src) – put the code here or make another thread ( src is better )
filesscanned = True
invoke-MsgBox("Scanning completed")
invoke If check_delete.Checked Then
If filesscanned Then
Invoke-ProgressBar1.Visible = True
Invoke-ProgressBar1.Minimum = 0
Invoke-ProgressBar1.Maximum = 100
Invoke-ProgressBar1.Refresh()
End If
checkdir(src) – put the code here or make another thread ( src is better )
invoke-MsgBox("Copying completed")
If filesfailed > 0 Then
Invoke-MsgBox("Warning: " + Str(filesfailed) + " files were not copied successfully.")
End If
guistop()– put the code here or make another thread ( src is better )
End sub
** DO THIS FOR ANY Delegat / invoke needed
for threads calls with parameters
trd_copy.ParameterizedStart(src)
Delegate Sub nameofDelegate(s As Integer)
Sub nameofDelegate+NameofSub(ByVal s As Integer)
If Form1.ProgressBar1.InvokeRequired Then
Dim d As New nameofDelegate (AddressOf nameofDelegate+NameofSub)
NameOfYourForm.Invoke(d, New Object() {s})
Else
If s = 1 Then
NameOfYourForm.ProgressBar1.Refresh() ** Or other Gui Functions
Else
End If
End If
End Sub
For Thread calls without parametrs
trd_copy.Start()
Delegate Sub nameofDelegate()
Sub nameofDelegate+NameofSub()
If Form1.ProgressBar1.InvokeRequired Then
Dim d As New nameofDelegate (AddressOf nameofDelegate+NameofSub)
NameOfYourForm.Invoke(d, New Object())
Else
NameOfYourForm.ProgressBar1.Refresh() ** Or other Gui Functions
End If
End Sub
I have a weird exception that I can't seem to debug. For anyone about to suggest worker threads I am prohibited from using them at my work... I asked why not myself and received a vague answer and clear directions to avoid it... anyways...
I decided I wanted to have a progress bar on a separate form that is initialized and shown directly from my library class (independent of the main form and executing on a different thread). Even though this class initializes the form itself, I am using control.InvokeRequired and control.Invoke anyway so I can reuse the form elsewhere.
The following is for initializing the progress bar:
Public Sub InitializePB(ByVal _Maximum As Integer, ByVal _Step As Integer, ByVal _StartValue As Integer, ByVal _Style As Windows.Forms.ProgressBarStyle)
If Me.InvokeRequired Then
'Different thread - invoke delegate
pb_Progress.Invoke(Sub() InitializePB(_Maximum, _Step, _StartValue, _Style))
Else
'Same thread - set values
pb_Progress.Minimum = 1
pb_Progress.Maximum = _Maximum
pb_Progress.Step = _Step
pb_Progress.Value = _StartValue
pb_Progress.Style = _Style
Me.Refresh()
End If
End Sub
Now for the step function:
Public Sub PerformStepPB()
If Me.InvokeRequired Then
'Different thread - invoke delegate
pb_Progress.Invoke(Sub() PerformStepPB())
Else
'Same thread - perform step
pb_Progress.PerformStep()
Me.Refresh()
End If
End Sub
Now the following is test code from where I launch form containing the progress bar and then send step calls:
pbForm = New frmProgress(_OwnerDesktopLoc)
pbForm.Show()
pbForm.InitializePB(100, 1, 1, Windows.Forms.ProgressBarStyle.Blocks)
Dim msElapsedVals As Integer = 0
While msElapsedVals <= 100
pbForm.PerformStepPB()
Thread.Sleep(100)
msElapsedVals += 1
End While
pbForm.Close()
pbForm.Dispose()
While Not pbForm.IsDisposed
Thread.Sleep(100)
End While
Exit Sub
So far so good... you will notice that I call the Form.Refresh method for the progress bar form every time the step method is called. I ended up doing this because when the form launched it would crash after several progress bar steps. At this point in time the form does show without crashing (because of the refresh) except that if I click the form it crashes (and no exception is caught by Visual Studio). The weird thing is that the code from the library class for updating the progress bar continues without any problems as if the problem is simply with the Win32 window. Here is the Windows message associated with the crash:
Description:
A problem caused this program to stop interacting with Windows.
Problem signature:
Problem Event Name: AppHangB1
Application Name: DL_RDS_Sort.vshost.exe
I tried launching the form from the main form thread but with the same results. I have no clue what's going on... any help would be greatly appreciated =D
Update on exactly how things are being called:
From MainForm a thread is started with the address of a subroutine defined within a library class :
Private Sub but_Sort_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles but_Sort.Click
Me.Sorter.OwnerDesktopLoc = New Drawing.Point(Me.DesktopLocation.X + CInt(Me.DesktopBounds.Width / 6), Me.DesktopLocation.Y + CInt(Me.DesktopBounds.Height / 2))
'Start sorting thread
Me.sortThread = New Threading.Thread(AddressOf Me.Sorter.Sort)
Me.sortThread.Start()
Then in this thread's routine I am testing the initialization of the form and calling the methods (shown earlier) I wrote for updating the performance bar properties:
Public Sub Sort()
Try
IIf(_OwnerDesktopLoc = Nothing, useProgressBar = False, useProgressBar = True)
'TESTING//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
pbForm = New frmProgress(_OwnerDesktopLoc)
pbForm.Show()
pbForm.InitializePB(100, 1, 1, Windows.Forms.ProgressBarStyle.Blocks)
Dim msElapsedVals As Integer = 0
While msElapsedVals <= 100
pbForm.PerformStepPB()
Thread.Sleep(100)
msElapsedVals += 1
End While
pbForm.Close()
pbForm.Dispose()
While Not pbForm.IsDisposed
Thread.Sleep(100)
End While
Exit Sub
'TESTING//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Note that there is a lot more code following the testing area but it never gets called and does not play a part in this issue because I exit the subroutine. I normally use ShowDialog but this halts the execution which is not what I want.
Some things worth mentioning:
-the frmProgress has the ControlBox property set to false.
-the frmProgress has the FormBorderStyle set to FixedDialog
Also: When I use the Show() method, the Windows "busy circle" cursor appears when I hover my mouse over the shown form. ShowDialog() does not have this behaviour... It's as if the form is waiting for something... Hope this clarifies my problem.