In VB.NET use a textbox as a log for which if statement it is beeing proccesed inside a sub - vb.net

Hi i have a Sub that has multiple if statements in it.
Each if statement has a large loop that searches for specific files and text inside files.
I tried various ways to use a text box in order to get the information which if is currently proccessing at the time and i see that for some reason the ui is not refreshed until the sub finishes and so i see everytime in the textbox the last proceesed if message.
What do you think is the best way to handle it?
I hope that this has nothing to do with threads because threads are something that i am not familiar with !

I think using Application.DoEvents() is an easy choice. But I don't know if that would be the desired behavior.
If the use of Application.DoEvents() fails, another thread should handle it.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.application.doevents?view=netframework-3.5

I use this:
Public Sub logWithCrLf(tx As TextBox, s As String)
tx.AppendText(s & vbCrLf)
tx.Select(tx.TextLength - 1, 0)
tx.ScrollToCaret()
tx.Refresh()
End Sub
I see that it scrolls a bit smoother using tx.AppendText(s) than tx.Text &= s, which scrolls up to 0 then down to caret again.
(I write this as an answer to contribute with the tx.AppendText() recommendation)

Related

Multithreading Webbrowsers

I am currently making a vb program that i plan to make very big. I have a decent knowledge of visual basic but today i came across something i do not understand. Because of the huge size of my program , i decided to try and keep the program as organized as possible by putting specific subs in modules. These subs consist of httprequest , webbrowsers(control), webclients and alot of loops. In order to prevent these subs from lagging my main application i thread them using threading.thread and i start them from my main form. But this leads to two problems.
Problem 1: The threads cannot in any way interact with the main form.
Once the a httprequest or webclient collects the information from my desired website, i am trying to make it add the info to a listbox in my main form, So what i did is it typed
Msgbox("Info Sent")
form1.listbox1.items.add(String)
The first messagebox will show but although the code right under it runs, nothing is added to the first forms listbox.I am not using delegates to transfer the information, instead, although its not a good habit, i am using checkforillegalcrossovers.
Problem 2: Threading with a webbrowser.
Threading with a webbrowser using threading.thread also does not work because it causes an active x error. After looking it up i found that a solution was to use a single threaded apartment but this would not work because i may need multiple threads running off the same sub at once.
One solution that i have found to this problem is creating another form completely and setting it invisible, and since the form is its own thread i do not need to use threading.thread , but the problem comes when i am trying to create multiple threads, or else i can somehow dynamically create the threads and put the subs inside of it programically this method wont work And even if it does i feel that it is sloppy so i will leave this for one of two last resorts.
The other solution is the most simple one in which i just put all of the code in the main form, but if i keep on doing that form1 is gonna become huge and sloppy, doing this wont solve the webbrowser problem either and even when using regions i still feel that something that 1000+ lines deserves its own class.
There must be some solution out there that solves these problems. Any help would be appreciated, Thanks.
I checked my code for updating the progress bar, and using a single thread with synclock will NOT work. They way I make it work is perform the step of the pbar each time after a thread is started as I have limited total threads (say less than 5 threads). Thus, even the progress bar steps before the threads are finished, but it will not progress further before new threads started. It is not 100% accurate but it more or less telling the progress
'update the progress bar
some_form.PBar1.PerformStep()
' This while loop is to count the existing running thread,
' and determine whether new thread should start
While 1
Dim t2 = New System.Threading.Thread(Sub() WaitForPermission())
t2.Start()
t2.Join()
If proceed_gen Then
Exit While
End If
End While
'Start doing what I need to do
Dim t1 = SomeSub()
t1.Start()
'End of code, as VB doest not have thread.detach()
Correct me if I am wrong, but you probably have to use a background worker. I know this is annoying, but this is the limitation of VB.net.
Or, you can have something like this (pseudo code, not tested)
structure some_struct
'define the strings you want to update, and their status such that
'main() knows if you need to update the stuff to the form
' You can also put how many threads are running, and the status of each thread,
'such that the main knows if all threads are completed
end structure
sub your_sub()
'Manipulate the website, and update the data structure with
'proper stuff you need
end sub
sub main(){
dim t1 = New System.Threading.Thread(Sub() your_sub())
t1.start()
' I am listing only one threads here, but start as many as you want
'check if there are strings that you need to update to the form
while 1
'check if there are any stuff you want to update from the data structure.
' Make sure you use synclock on the data structure, so each thread won't fight each other on accessing the data struct
dim should_update as boolean = false
should_update = 'Something thatyou should implement to judge whether you should update the form.
'You can start a thread and join it so the thread won't fight with other threads for accessing the data structure
dim some_string as string
if should_update
some_string = 'You may also need a thread to join to get the stuff you need. Dim the string as an array if it is required.
'You can also try pass by ref
'if you need to use thread to access the data structure to know if you need to update the form
form1.listbox1.items.add(some_string )
end if
end while
end sub
This is an ugly solution, but it will help you do the job...

How do I pass Form1.TextBox1.Text to Form2.TextBox2.Text using Visual Basic (correctly)?

This is a continuation of a question asked two years ago in this thread:
VB6 equivalent of string.IsNullOrEmpty
(I think.) A programmer recommended I use the String.IsNullorEmpty method, which I used this thread for to convert to Visual Basic, but I still couldn't get it to work.
The specifics of my question are here, including all current code:
http://www.daniweb.com/software-development/visual-basic-4-5-6/threads/473930/passing-data-between-forms-in-vba
Here is the gist of it, copied directly from the second link:
So I'm trying to make a link between TextBox1.Text on Form1 to TextBox2.Text on Form 2. What I currently have is a line of code underneath my TextBox2_Change code reading:
TextBox2 = Form1.TextBox1.Text
This ALMOST does what I want it to do. The only problem is that it is requiring me to input any character in to the TextBox2 when Form2 pops up before it displays.
I'm trying to get that problem solved and then I'm eventually going to try to get it to chop off part of the file name until just the project file name displays.......but that's a whole different game I'll be playing. One step at a time.
Does anyone have any suggestions?
Explanation
You should write the code under the Form2_Load event.
If you write the code under Textbox2_TextChanged event, the code will be executed only when you type or delete something in Textbox2 (That is the same as Text being changed).
Code and Example
Private Sub Form2_Load () Handles Mybase.Load
Textbox2.Text = Form1.Textbox1.Text
End Sub
Hope it works perfectly!
The behaviour you observe is normal : your TextBox2 is only updated (with the value from TextBox1) ... when you update it manually (_Change).
Hey friend it very simple. you need not to add any kind of other functions just use dot(.) operator to access all components of Form1.
e.g.:
Form1.TextBox1.AppendText("hello")
or you can read value from Form2 and insert it into Form1.
e.g. :
Dim txt As String=TextBox1.Text
Form1.TextBox1.AppendText(txt)

How to CORRECTLY implement a multithreaded progressbar during a LINQ query?

Okay so here's the thing:
I have a linq query which loads approx. 1000 lines into a variable, during that process I want to display a progressbar, not necessarily stating the percentage, can be marquee style, doesnt matter.
This progressbar is on a modal form to precent the user from interacting with the app for the time the query's running.
Now here's my code:
Private Sub LoadBar()
Try
Dim load As New frmLoadbar
load.Text = "Loading bunch of data..."
load.ShowDialog()
Catch e As Threading.ThreadAbortException
Threading.Thread.ResetAbort()
End Try
End Sub
In another sub:
Dim myThreadDelegate As New Threading.ThreadStart(AddressOf LoadBar)
Dim th As New Threading.Thread(myThreadDelegate)
th.Name = "TimeConsuming"
th.Start()
Dim XY = db.Table.GetEnumerator
While XY.MoveNext
Dim item As New ListViewItem
item.Text = XY.Current.Name
item.Tag = XY.Current
ListBox1.Items.Add(item)
End While
Autos.Dispose()
Try
th.Abort()
Catch ex As Exception //here's where i 'swallow the re-thrown exception
End Try
Not thats one of the ugliest code i've ever written.It works i just dont want that rethrown exception.
Some explanation:
I want the modal form to close after the query is done.
For that reason I 'abort' the thread running the form.
Since aborting a thread throws a double-exception i have to 'swallow'
that exception.
Now i know i could implement this like the following:
Coding a loop into the form holding the progressbar, which checks
periodically for a boolean's value, and if its true the form could
close itself.
From the other form - on the worker thread - i could change that
booleans value to true after the query's finished.
But here comes my question:
Whats the best way to implement this?
I know it can be done with a background worker, which has been
specifically invented for this reason, but can i use the background
worker as the thread to show the progressbar?
If not (and i have to run the query on the background worker and
showing the modal form from my original form), would that mean that
the query would "work in the background"?
Would that mean that the query would be slower?
I've looked into other tutorials, but for one reason or another, either i wasnt able to copy it (due to complexity) or I wasn't convinced that it was better than this.
Thank you for your time you took to answer.
You could show the modal form and then run a BackgroundWorker from that form. The progress and completed events would be on the UI thread so you can update a progress bar while it is running and close the form in the completed event handler.
Okay, so for future reference, if someone needs clear help with code samples, Microsoft has it (thats a first..)
You can download it here:
Multithreading
Assuming Windows Forms, you do this with a BackgroundWorker component.

Is there a DocumentAfterPrint event in MS Word?

I am working on a project where I need to return a word document back to a certain state after it is printed. I have found a DocumentBeforePrint event but I cannot find a DocumentAfterPrint event.
Is it poorly documented or is there some other workaround that exists?
Here is one workaround based on subroutine names. I do not believe there is a specific DocumentAfterPrint event like you desire. Here's the code:
Sub FilePrint()
'To intercept File > Print and CTRL-P'
MyPrintSub
End Sub
Sub FilePrintDefault()
'To intercept the Standard toolbar button'
MyPrintSub
End Sub
Sub MyPrintSub()
Dialogs(wdDialogFilePrint).Show
'Your code here, e.g:'
MsgBox "I am done printing."
End Sub
UPDATE: Please note the gotchas in Will Rickards' answer below.
Looking at the application events I don't see it.
I don't see it in the document events either.
Please note on the workaround provided above, that is using the FilePrint and FilePrintDefault methods, you should read this site. These methods replace the built-in function. So you actually need to add code in there or have it generated for you to get word to actually print.
Also background printing can cause your code to execute before it has finished printing. If you really must run something after it has printed you'll need to disable background printing.
I don't believe any of the suggested work-arounds will work in Word 2010. However, I have had success by using the application.onTime() method at the end of the documentBeforePrint event to cause another procedure to be executed a few seconds later.

Profiling VBA code for microsoft word

I have some legacy code that uses VBA to parse a word document and build some XML output;
Needless to say it runs like a dog but I was interested in profiling it to see where it's breaking down and maybe if there are some options to make it faster.
I don't want to try anything until I can start measuring my results so profiling is a must - I've done a little searching around but can't find anything that would do this job easily. There was one tool by brentwood? that requires modifying your code but it didn't work and I ran outa time.
Anyone know anything simple that works?
Update: The code base is about 20 or so files, each with at least 100 methods - manually adding in start/end calls for each method just isn't appropriate - especially removing them all afterwards - I was actually thinking about doing some form of REGEX to solve this issue and another to remove them all after but its just a little too intrusive but may be the only solution. I've found some nice timing code on here earlier so the timing part of it isn't an issue.
Using a class and #if would make that "adding code to each method" a little easier...
Profiler Class Module::
#If PROFILE = 1 Then
Private m_locationName As String
Private Sub Class_Initialize()
m_locationName = "unknown"
End Sub
Public Sub Start(locationName As String)
m_locationName = locationName
MsgBox m_locationName
End Sub
Private Sub Class_Terminate()
MsgBox m_locationName & " end"
End Sub
#Else
Public Sub Start(locationName As String)
'no op
End Sub
#End If
some other code module:
' helper "factory" since VBA classes don't have ctor params (or do they?)
Private Function start_profile(location As String) As Profiler
Set start_profile = New Profiler
start_profile.Start location
End Function
Private Sub test()
Set p = start_profile("test")
MsgBox "do work"
subroutine
End Sub
Private Sub subroutine()
Set p = start_profile("subroutine")
End Sub
In Project Properties set Conditional Compilation Arguments to:
PROFILE = 1
Remove the line for normal, non-profiled versions.
Adding the lines is a pain, I don't know of any way to automatically get the current method name which would make adding the profiling line to each function easy. You could use the VBE object model to inject the code for you - but I wonder is doing this manually would be ultimately faster.
It may be possible to use a template to add a line to each procedure:
http://msdn.microsoft.com/en-us/library/aa191135(office.10).aspx
Error handler templates usually include an ExitHere label of some description.. The first line after the label could be the timer print.
It is also possible to modify code through code: "Example: Add some lines required for DAO" is an Access example, but something similar could be done with Word.
This would, hopefully, narrow down the area to search for problems. The line could then be commented out, or you could revert to back-ups.
Insert a bunch of
Debug.Print "before/after foo", Now
before and after snippets that you think might run for long terms, then just compare them and voila there you are.
My suggestion would be to divide and conquer, by inserting some timing lines in a few key places to try to isolate the problem, and then drill down on that area.
If the problem is more diffused and not obvious, I'd suggest simplifying by progressively disabling whole chunks of code one at a time, as far as is possible without breaking the process. This is the analogy of finding speed bumps in an Excel workbook by progressively hard coding sheets or parts of sheets until the speed problem disappears.
About that "Now" function (above, svinto) ...
I've used the "Timer" function (in Excel VBA), which returns a Single.
It seems to work just fine. Larry