Showing form from form_load event handler and showing progressbar - vb.net

Here are some things which I don't know how to do properly in .Net but I'm sure that some solution surely exists.
I have form which is open like dialog and under (that) form_load I have some checks of data and after that I call a procedure which may take 10 seconds to process.
During this time my form is not showed until my process don't finish and I can see some 'garbage of menu' in my main form instead of progressbar in those new form which is in process.
Second thing I see and which may be close to first problem is that in some of my forms progressbar don't comes to end when results of some process is showed but program (for progressbar) is maked properly. It seem's like progressbar work in some asynchronous task.
How to fix those problems and get my form showed before my procedure starts?
How to get progressbar to show a value reliable? Or I can say beter like it works in VB6?
For first problem I try:
Me.Activate
or
Me.Refresh
Call myProcedureWithProgressBar(myArgs)
From Form_Load but without results.
Form is showed when my procedure finishes but should be showed before.

Event Form_Load executes the code before showing the Form. You can create a Timer and use this to automatically execute code after form load:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Timer1.Start()
End Sub
Private Sub OnFormLoaded(sender As Object, e As EventArgs) Handles Timer1.Tick
Timer1.Enabled = False
'...
End Sub
And make sure you stop the timer on the method like I did, and make sure you prevent user to do something that could interfiere with the execution (clicking buttons, closing form...). Hope it helps.
And about the ProgressBar issue, I don't know what could be the problem without more information or code. Maybe you can try something like this:
Public Sub UpdateProgressBar(ByVal cont As Integer, ByVal max As Integer, ByRef objTarget As ProgressBar)
Dim dProgress As Double = cont * 100 / max
objTarget.Value = CInt(dProgress)
objTarget.Refresh()
End Sub

Related

Most efficient way to programmatically update and configure forms and controls

I am looking for a way to prevent user form controls from appearing one by one when I'm programmatically adding them and for ways to enhance application performance and visual appeal.
Say, I have a Panel_Top in which I programmatically add comboboxes. What is happening is that they appear one by one as they are created and I am looking for a way to suspend the refreshing of the panel and or user form to make all of those programmatically added comboboxes to appear at the same time and faster than it happens right now.
I've tried suspendlayout which doesn't do anything for me or maybe I'm doing it wrong.
MyForm.PanelTop.SuspendLayout = true
And also I've tried to set the Panel_Top to invisible like:
MyForm.Top_Panel.visible = false
Which kind of sorta looks and performs better, or it might be a placebo.
What is the correct approach to this problem?
PS: I do have form set to doublebuffer = true, if that matters
What I tend to do is create a loading modal to appear on top of the form rendering the controls that need to be created/made visible, this can optionally have a progress bar that gets incremented as the control is created/shown. With the loading modal running, the container that needs to add the controls starts with SuspendLayout, adds the controls, and then finished with ResumeLayout.
This makes it so that controls are added/shown while giving the user a visual indicator that something is going on behind the scenes.
Here is a phenomenal example of a loading modal: https://www.vbforums.com/showthread.php?869567-Modal-Wait-Dialogue-with-BackgroundWorker and here is an example of using it:
Private ReadOnly _controlsToAdd As New List(Of Control)()
Private Sub MyForm_Show(sender As Object, e As EventArgs) Handles MyBase.Shown
Using waitModal = New BackgroundWorkerForm(AddressOf backgroundWorker_DoWork,
AddressOf backgroundWorker_ProgressChanged,
AddressOf backgroundWorker_RunWorkerCompleted)
waitModal.ShowDialog()
End Using
End Sub
Private Sub backgroundWorker_DoWork(sender As Object, e As DoWorkEventArgs)
Dim worker = DirectCast(sender, BackgroundWorker)
For index = 1 To 100
_controlsToAdd.Add(New ComboBox() With {.Name = $"ComboBox{index}"})
worker.ReportProgress(index)
Threading.Thread.Sleep(100) ' Zzz to simulate a long running process
Next
End Sub
Private Sub backgroundWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
Dim percentageCompleted = e.ProgressPercentage / 100
' do something with the percentageCompleted value
End Sub
Private Sub backgroundWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
PanelTop.SuspendLayout()
PanelTop.Controls.AddRange(_controlsToAdd.ToArray())
PanelTop.ResumeLayout()
End Sub
SuspendLayout() is the correct way to handle this with WinForms.
But first of all, this is a function you call, and not a flag you set.
Secondly, don't forget to call ResumeLayout() at the end of the changes.
Finally, you need to ensure you only call them once when you start to change around the controls in the panel and then again at the very end. If you use them with every control you won't get any benefit.
So the pattern might look something like this:
Public Sub SomeMethod()
PanelTop.SuspendLayout() ' Prevent the panel from updating until you've finished
' Make a bunch of changes
PanelTop.Controls.Clear()
For Each ...
PanelTop.Controls.Add( ... )
Next
PanelTop.ResumeLayout() ' Allow the panel to show all the changes in the same WM_PAINT event
End Sub
You also need to ensure you don't have anything in there like DoEvents()/Invalidate() that might invoke the windows message loop and cause the form to redraw itself.

VB.net PrintForm Not Working in New Thread

I am developing a e-filing app and I need to print an adhesive label with some info to attach to the physical folder.
I already designed the label as a Form put the logo and everything that I need there. Then on the Form.Shown event I put the command to print:
Me.PrintLabelForm.Print() (This is VisualStudio PowerPack Control)
And here is where I bump into a problem. The print out is totally empty (I already changed margins setup the printer, etc). The issue is that the form is not actually fully loaded, I switch the method to the print preview and the controls are there but they are empty.
I tried several approaches but I have been not able to do this automatically. One solution that I found was to have a button to do the Me.PrintLabelForm.Print() then it works because the form is already fully loaded and displayed but this is not an option. I need the form to open automatically, print and close.
An option that I think it should work will be to have a new thread with a timer then printing so I did this:
Private Sub LabelPrint_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub LabelPrint_Shown(sender As Object, e As EventArgs) Handles Me.Shown
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Left = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Right = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Top = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Bottom = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Landscape = True
Dim PrintThread As New System.Threading.Thread(AddressOf PrintSub)
PrintThread.Start()
End Sub
Private Sub PrintSub()
Threading.Thread.Sleep(1000)
Me.PrintLabelForm.Print()
Me.Close()
End Sub
The idea was to have the PrintSub to give the app enough time to finish to render the whole thing then print but I am getting this error:
**An unhandled exception of type 'System.Exception' occurred in Microsoft.VisualBasic.PowerPacks.dll
Additional information: The window being printed must be visible and contain focus.**
So I wonder how to make this thread have the window form in focus in order to be able to print.
That is all. Thanks for all the help.
Always work with the form only from main thread.
You found it right – form printing will not run from new thread.
When you do any actions on forms, you must perform all the work from Dispatcher thread. It is the thread on which all event methods run. If you fail doing so, you can encounter many side effects. (Not only problem with printing. I've been there and this advice from senior Windows programmer helped me to get things back to normal.) So do not use form printing from any other thread.
If you want a workaround for this, print form to the image (in main thread) and then you can print the image using new thread.
This has nothing to do with .NET, this is related to internals of Windows Forms technology. Welcome to Windows programming.
I manage to solve it putting this line in the Form.Shown
PrintLabelForm.Print(Me, PrintForm.PrintOption.ClientAreaOnly)
I don't know why or how but it works.
Thanks to all of you guys for your help. Let's hope I don't find myself trying to do stuff when the form is fully displayed.
This is my full code let's hope it works for someone else:
Imports Microsoft.VisualBasic.PowerPacks.Printing
Public Class PrintAdhesiveLabel
Private Sub LabelPrint_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub LabelPrint_Shown(sender As Object, e As EventArgs) Handles Me.Shown
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Left = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Right = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Top = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Margins.Bottom = 0.1
PrintLabelForm.PrinterSettings.DefaultPageSettings.Landscape = True
PrintLabelForm.Print(Me, PrintForm.PrintOption.ClientAreaOnly)
Me.Close()
End Sub
End Class
Perhaps this is relevant:
Only the form that currently has focus can be printed by using this
method. If you have set the Form property to another form before
calling this method, the image of the form may not be rendered as
expected. To avoid this, call the Focus method of the form before you
call Print.
So call Me.PrintLabelForm.Focus() before calling Me.PrintLabelForm.Print():
Private Sub PrintSub()
Threading.Thread.Sleep(1000)
Me.PrintLabelForm.Focus()
Me.PrintLabelForm.Print()
Me.Close()
End Sub

winform and session timeout

I have a vb.net winform and I want to know how to add sort of like a session time out to it. For example, I have a varialbe set to 10 min, within that 10 min, if there is no activity (no mouse/no keyboard interaction), I would like to log the user out. Can anyone shine some light on this subject on how to make this work?
First question, why do you want to do in a winform. Such things we generally use in web forms. But even you want to use such things in WinForms you need to use Timer Class.
Whenever you encounter activity, you can just reset the timer by calling Stop then immediately calling Start. Place whatever code you'd like in the Timer's Tick event (assuming this is a System.Windows.Forms.Timer) and you'll be all set.
I'd suggest you use the event Application.Idle.
No need to P/Invoke.
Public Class Form1
Private WithEvents _timer As Timer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' 10 seconds for testing
Me._timer = New Timer With {.Interval = 10000, .Enabled = True}
AddHandler Application.Idle, AddressOf Me.Application_Idle
End Sub
Private Sub Application_Idle(sender As Object, e As EventArgs)
Me._timer.Stop()
Me._timer.Start()
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles _timer.Tick
Me._timer.Stop()
RemoveHandler Application.Idle, AddressOf Me.Application_Idle
' Do something to log the user out
Me.Close()
End Sub
End Class
If you are looking for a way to detect input outside your application Amit's suggestion will not work.
See Detecting idle users in Winforms if that is the case. Calling GetLastInputInfo() and checking the last input value should give you something to go off.
If you are not worried about the user leaving your application, and getting logged out after not using it, use Amit's way of resetting a timer on the input event.

Timer does not work

I try to run a timer from my winform application. For some reason the function that should run on the timer's tick (IsTimeOffsetValid) is not called nor stopped on break point, and basically nothing happens. I attached a code sample below.
I appreciate the help.
Module Module1
Sub main()
Dim OutputForm As New Form17
Application.Run(OutputForm)
End Sub
End Module
Public Class Form17
Private TimerServerOffset As New System.Timers.Timer
Private Sub Form17_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
AddHandler TimerServerOffset.Elapsed, New System.Timers.ElapsedEventHandler(AddressOf IsTimeOffsetValid)
TimerServerOffset.Interval = 1
TimerServerOffset.Start()
End Sub
Private Sub IsTimeOffsetValid()
MsgBox("IsTimeOffsetValid")
End Sub
End Class
Apart from errors in the code that you posted there are other issues with the design.
Read this question: System.Timers.Timer vs System.Threading.Timer
The callback is called on a worker thread (not the UI thread) so displaying a message box could be a big problem.
then switch to a more fitting timer. If all you want to do is validate the inputs every second, switch to the System.Windows.Forms.Timer. The tick handler runs on the UI thread so you can change the UI in the handler.
Then consider changing the interval a message box popping up every millisecond is not possible and not user friendly.
Finally, I would suggest NOT using a timer for this: just handle changes to the input fields and respond to changed inputs or use the standard validation events of the WinForms controls. This is much cheaper (on the CPU) and will not mess with the focus.

Threading issue in Vb.net

I create vb.net application and i add one windows form,called frmScan.
i put two textboxes and two labels. Then i write the following
thread with delegate event.
Private Delegate Sub DoInitializedDelegate()
Public motdet As New Thread(AddressOf MotionDetection)
Private Sub MotionDetection()
'Do motion detection Work
'It is never ending Loop until form unload.
End Sub
Then I start it in my form load event.
Private Sub frmScan_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
motdet.Start()
End Sub
So my problem started when i load the form.
i can see the form but it is like loading something.
i thought my motion detecting thread is never ending loop until form unload.
i cannot type anything inside two text box i mentions in above.
how should i do ?
Use Application.DoEvents() before calling your long running thread.
Refer example here http://msdn.microsoft.com/en-us/library/aa446540.aspx