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
Related
I have a bit of an oddball problem. I use a routine to check focus; and ... it stopped working. I have no idea how or why.
The routine basically checks to see if the active control is the one you're checking, and if so, returns true (so we can handle the cases where it's not).
However...it recently started returning false all the time (we didn't change anything, we only noticed when some field auditing started returning weird values). Even when the control is focused, and if there's no other controls on the form, or only one form open, and the form clearly has focus.
Does anyone have any ideas how or why this might be? It's confounding me. As you can see, I've got a test field, where we're running an init in it...and the values clearly match, name, values, every field compared, and it still doesn't return true.
What am I doing wrong?
Edit: forgot to add the code.
The whole thing as-is:
' I call it from here:
' Inside form, any control, say `PurchaseCostBox`
Private Sub PurchaseCostBox_AfterUpdate()
' Check if the focus is had
If VerifyFocus(Me.PurchaseCostBox) Then
' Save more field info.
Debug.Print Me.PurchaseCostBox.SelStart
Debug.Print Me.PurchaseCostBox.SelLen
Debug.Print Me.PurchaseCostBox.Value
Else
' Do limited stuff
Debug.Print Me.PurchaseCostBox.Value
End if
End Sub
Public Function VerifyFocus(ByRef ctlWithFocus As Control) As Boolean
Dim FrmParent As Form
Dim ctlCurrentFocus As Control
On Error Resume Next
' Determine parent form for control
' Verify focus of parent form
Set FrmParent = Screen.ActiveForm
' Verify focus of control on form
Set ctlCurrentFocus = FrmParent.ActiveControl
If Not ctlCurrentFocus Is ctlWithFocus Then
ctlWithFocus.SetFocus
DoEvents
End If
' Even adding the below line does not return true:
ctlWithFocus.SetFocus
' Return true if the control currently has the focus
VerifyFocus = FrmParent.ActiveControl Is ctlWithFocus
' Discard any errors
Err.Clear
End Function
I've also had it try this:
Public Function VerifyFocus(ByRef ctlWithFocus As Control) As Boolean
On Error Resume Next
' Return true if the control currently has the focus
VerifyFocus = Screen.ActiveControl Is ctlWithFocus
' Discard any errors
Err.Clear
End Function
Neither work any more...and I'm floundering.
Well, this turned out to be something utterly unexpected, and totally unrelated to the focus.
Turns out, one of the ways I call this is by getting a control's parent, by using Control.Properties.Parent.Form. While this DOES return the correct form, it also makes the above VerifyFocus routine never return true, ever (even when it's not being used). I don't know why. I really, at this point, don't care. But I'm going to leave it here for others to find.
Refactoring my GetTopForm routines allowed me to get the focus.
I am writing a library to automate internet explorer. The library sets up its own message loop by starting a thread and using Dispatcher.Run. The reason for this is to keep internet explorer and mshtml on the same thread.
I then use the following in the library.
Private ExDispatcher As Dispatcher
Private Success As Boolean
Private EXThreadWaiter As Threading.AutoResetEvent
Public Async Function ClickAndWaitForNewPageAsync() As Task(Of Boolean)
Return Await Task.Run(Of Boolean)(Function()
ExDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, Sub() startClickAndWaitForNewPage())
EXThreadWaiter.WaitOne()
Debug.Print("Completed with: " + Success.ToString)
Return Success
End Function)
End Function
Private Sub startClickAndWaitForNewPage()
'do a lot of stuff and wait for internet explorer
'if we are happy and all good set Success = True
'now let the thread in the task go
EXThreadWaiter.Set()
End Sub
ClickAndWaitForNewPageAsync starts a task which is on a new thread, it immediately then uses my message loop to call startClickAndWaitForNewPage and all the Internet Explorer stuff is done. When everything is ok the variable Success is set to a value and EXThreadWaiter.Set() is called which releases the task thread and the result is returned.
A client of this library therefore uses it as shown below.
Private Async Sub someFunction()
Dim res As Boolean = Await ClickAndWaitForNewPageAsync()
If res Then
'continue on with code
Else
'do nothing
End If
End Sub
The issue I have with this is how to shutdown or stop my library which needs to happen for various reasons. I currently have this method in the same class as ClickAndWaitForNewPageAsync
Public Sub StopIt()
Success = False
EXThreadWaiter.Set()
End Sub
This method sets Success to False and lets the task continue. This mostly works fine but it is possible that the following happens.
startClickAndWaitForNewPage sets Success to True
EXThreadWaiter.Set() is called
Debug.Print("Completed with: " + Success.ToString) indicates Success is True
StopIt() runs setting Success = False
someFunction in the client code receives Success = True
This means that in someFunction it continues with its code thinking everything is ok but in fact it is not.
How can I prevent this from happening?
By the way the Cancellation Token etc i do not think will help, but I'm happy to hear differently.
What I previously used
I previously did not use Tasks. I would simply use the Dispatcher begininvoke which would mean everything is done on the right thread and the client would have to subscribe to an event that would fire when everything was done successfully. This felt untidy to me and thus I am trying this new method.
I'm attempting to create a checkbox that will pause the current function when clicked. Here's how my code is set up-
Public Function Wait()
LogOutput("Application paused.")
Do Until waitBox.Checked = False
'Task.Delay(1000)
My.Application.DoEvents()
Loop
LogOutput("Application unpaused.")
Return Nothing
End Function
Public Function Thing()
For Each i In x
If waitBox.Checked Then
Wait()
Else
DoStuff()
End If
Loop
Return Nothing
End Function
As you can see I've tried "Task.Delay" as well as "DoEvents(), however the problem is that the GUI is locked up while the function is running and so the user is unable to click pause at all.
How can I get around this?
Thanks in advance.
You need to create a new thread for that, so the GUI is not locked:
https://msdn.microsoft.com/en-us/library/system.threading.thread(v=vs.110).aspx
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
Objective: Redirect focus from one command button to another using the first's GotFocus procedure.
Context: I have a form-independent procedure in a generic module that, on most forms, sets focus to the NewRecord button after saving the previous record. But on one form, I would like to redirect (based on certain conditions) focus back to the SignRecord button so the user can "sign" a second part of the same record (I may need this for other uses in the future). The target control is enabled and visible and can otherwise be focused and the original control can be focused when the redirect doesn't occur. Reference [2] below implies that this should be possible, though I'm not changing visibility of my controls.
Issue: When the conditions are met to redirect focus in the GotFocus procedure, it redirects as desired but the original (test) SetFocus call throws a "Run-time error '2110', Can't move focus to the control CommandNew".
What I've tried:
Exit Sub after my downstream SetFocus calls.
Call CommandSign.SetFocus in the hopes that it would make it happen outside the previous SetFocus process.
In a module,
Public Sub test()
Forms("TargetForm").CommandNew.SetFocus 'This gets the error '2110'
End Sub
In the 'TargetForm',
Private Sub CommandNew_GotFocus()
If IsNull(textDateTime) Then Exit Sub 'Works as expected
'I can see these two parts work. The framSign value changes
'and CommandSign gets focus
If checPPC And IsNull(textSigID_PPC) And framSign = 2 Then
framSign = 1
CommandSign.SetFocus
ElseIf checDAS And IsNull(textSigID_DAS) And framSign = 1 Then
framSign = 2
CommandSign.SetFocus
End If
End Sub
References:
[1]: SelectNextControl() a bad idea in a GotFocus event?
[2]: http://www.access-programmers.co.uk/forums/showthread.php?t=100071
I think your problem is that the call to Forms("TargetForm").CommandNew.SetFocus doesn't quite seem to, in fact, finish setting the focus to CommandNew until after Private Sub CommandNew_GotFocus() has finished executing. Because you've called another SetFocus before the first SetFocus could finish, there is a conflict that Access seems to be unable to cope with.
Whether or not that is the case, one thing is clear: the way you have your execution plan set up right now is unfortunately not going to work. You might try adding either a global variable or a public variable to each form that determines whether or not you should set your focus to CommandSign after you set the focus to CommandNew.
Ex. TargetForm:
Public boolSetCommandSignFocusInstead As Boolean
Private Sub CommandNew_GotFocus()
If IsNull(textDateTime) Then Exit Sub 'Works as expected
'I can see these two parts work. The framSign value changes
'and CommandSign gets focus
If checPPC And IsNull(textSigID_PPC) And framSign = 2 Then
framSign = 1
boolSetCommandSignFocusInstead = True
ElseIf checDAS And IsNull(textSigID_DAS) And framSign = 1 Then
framSign = 2
boolSetCommandSignFocusInstead = True
Else
boolSetCommandSignFocusInstead = False
End If
End Sub
Module:
Public Sub test()
Forms("TargetForm").CommandNew.SetFocus
If Forms("TargetForm").boolSetCommandSignFocusInstead Then
Forms("TargetForm").CommandSign.SetFocus
End If
End Sub