Bug on login form while trying to close the app vb.net - vb.net

So currently I am using a code which provides me the possibility to manager whenever or not the application will logout or keep on track. Basically if the user is not using the program for quite some time it will move to the Login form and of course if the user wants to login back he had to type again.
I got a class where I am able to produce more or less that code:
Public Class TimerWatcher
Private _timer As System.Threading.Timer
Private _enabled As Boolean
Private _lastEvent As DateTime
Public Sub New()
_timer = New System.Threading.Timer(AddressOf watch)
_enabled = False
Timeout = 0
End Sub
Public Event Idle(sender As Form)
Public Property Timeout As Long
Public Property Enabled As Boolean
Get
Return _enabled
End Get
Set(value As Boolean)
If value Then
_lastEvent = DateTime.Now
_timer.Change(0, 1000)
Else
_timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite)
End If
End Set
End Property
Private Sub watch()
If DateTime.Now.Subtract(_lastEvent).TotalMilliseconds > Timeout Then
Enabled = False
' "End" is quite blunt. You may want to raise an event
' so the form can terminate the application gracefully.
RaiseEvent Idle(Login)
End If
End Sub
Public Sub Refresh()
_lastEvent = DateTime.Now
End Sub
End Class
And then on my main form I got this:
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
watcher.Timeout = 5000
watcher.Enabled = True
End Sub
Private Sub PaginaInicial_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
watcher.Refresh()
End Sub
Private Sub watcher_idle(sender As Form) Handles watcher.Idle
Dim logIN = New Login
If MsgBox("The application will close, are you sure you want to do it?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
logIN.Show()
Else
End If
End Sub
But, because some bug I am not able to return the Login form...And this is what happen to the form after I click yes on the MessageBox. The login form crash or something
Do you have any idea how I can solve this bug?

Related

Cleanly stopping a thread in VB.net to avoid double error handling

I’ve got this issue with stopping a thread cleanly. I’ve tried to simplify it into a more basic version of the code below and I’m wondering if my approach is completely wrong here.
I have Form1 with a bunch of UI elements which need updating as BackgroundCode runs (I run it here so it’s a separate thread and it doesn’t hold up the UI) I then update the UI by invoking a sub
(Me.Invoke(Sub()
something.property=something
End Sub))
I’m also trying to handle some errors handed to the application by an external file. I’ve used a timer to check for the file and if it exists I grab the contents and pass it to my ErrorHandler. This Writes the Error out to a log file, displays it on screen and then aborts the background worker so that the program doesn’t continue to run. The trouble I’m getting is that by executing BackgroundThread.Abort() that action itself is triggering the ErrorHandler. Is there a way to ask the BackgroundThread to stop cleanly? I want BackgroundThread to trigger the ErrorHandler if something else goes wrong in that code.
I’m wondering about using a global boolean like “ErrorIsRunning” to restrict the ErrorHandler sub so that it can only ever run once, but this is starting to feel more and more hacky and I’m wondering if I’ve gone completely off track here and if there might be a better way to approach the entire thing.
Public Class Form1
Dim BackgroundThread As New Thread(AddressOf BackgroundCode)
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
‘Hide Error Page
ErrorPage.Visible = False
ErrorLabel.Visible = False
‘Start Background Code
BackgroundThread.Start()
End Sub
Private Sub BackgroundCode()
Try
‘<Background code which runs over a number of minutes>
Catch.ex as Exception
ErrorHandler(“Error with BackgroundCode: “ + ex.Message)
End Try
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Dim ErrorFile As String = “C:\MyErrorFile.Err”
Dim ErrorContents As String
If File.Exists(ErrorFile) Then
Timer.Enabled = False
ErrorContents = File.ReadAllText(ErrorFile).Trim()
ErrorHandler(ErrorContents)
End If
End Sub
Public Sub ErrorHandler(ErrorText As String)
WriteLog(“ERROR” + ErrorText)
Me.Invoke(Sub()
Me.ErrorPage.Visible = True
Me.ErrorLabel.Text = ErrorText
End Sub)
BackgroundThread.Abort()
End Sub
End Class
Never abort threads.
This uses a Task and a ManualResetEvent. Without seeing the code inside of the background task it is hard to know how many stop checks might be needed.
Public Class Form1
Private BackgroundTask As Task
Private BackgroundTaskRunning As New Threading.ManualResetEvent(True)
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Hide Error Page
ErrorPage.Visible = False
ErrorLabel.Visible = False
'Start Background Code
BackgroundTask = Task.Run(Sub() BackgroundCode())
End Sub
Private Sub BackgroundCode()
Try
'<Background code which runs over a number of minutes>
'put stop checks periodically
' e.g.
If Not BackgroundTaskRunning.WaitOne(0) Then Exit Sub 'stop check
Catch ex As Exception
ErrorHandler("Error with BackgroundCode: " + ex.Message)
End Try
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Dim ErrorFile As String = "C:\MyErrorFile.Err"
Dim ErrorContents As String
If File.Exists(ErrorFile) Then
Timer.Enabled = False
ErrorContents = File.ReadAllText(ErrorFile).Trim()
ErrorHandler(ErrorContents)
End If
End Sub
Public Sub ErrorHandler(ErrorText As String)
WriteLog("ERROR" + ErrorText)
Me.Invoke(Sub()
Me.ErrorPage.Visible = True
Me.ErrorLabel.Text = ErrorText
End Sub)
BackgroundTaskRunning.Reset() 'stop task <<<<<<<<<<<<<<<<<<<<<<<<<<<
End Sub
End Class

Showing WinSCP .NET assembly transfer progress on WinForm's progress bar

Have some main form on which I am calling file downloading from FTP. When this operation is raised i want to see new form as ShowDialog and progress bar on it to be shown meantime, then show the progress and close new form and back to main form. My code is working however, when it will process is started my main form freezes and after while new form is appearing and then closing. What I would like to correct is to show this new form to be showed straightaway after process is executed. Can you take a look and tell me whats wrong?
This is out of my main form the download process called:
Dim pro As New FrmProgressBarWinscp(WinScp, myremotePicturePath, ladujZdjeciaPath, True)
FrmProgressBarWinscp is as follows:
Public Class FrmProgressBarWinscp
Property _winScp As WinScpOperation
Property _remotePicture As String
Property _ladujZdjecia As String
Property _removesource As String
Public Sub New()
InitializeComponent()
End Sub
Sub New(winscp As WinScpOperation, remotePicture As String, ladujzdjecia As String, removesource As Boolean)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_winScp = winscp
_remotePicture = remotePicture
_ladujZdjecia = ladujzdjecia
_removesource = removesource
ShowDialog()
End Sub
Sub Run()
Try
Cursor = Cursors.WaitCursor
_winScp.GetFile(_remotePicture, _ladujZdjecia, _removesource)
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = 1
ProgressBar1.Value = 0
Do
ProgressBar1.Value = WinScpOperation._lastProgress
ProgressBar1.Refresh()
Loop Until ProgressBar1.Value = 1
Cursor = Cursors.Default
'Close()
Catch ex As Exception
Finally
If _winScp IsNot Nothing Then
_winScp.SessionDispose()
End If
System.Threading.Thread.Sleep(10000)
Close()
End Try
End Sub
Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Run()
End Sub
End Class
Winscp my own class and used methods:
...
Function GetFile(source As String, destination As String, Optional removeSource As Boolean = False)
Dim result As Boolean = True
Try
session.GetFiles(source, destination, removeSource).Check()
Catch ex As Exception
result = False
End Try
Return result
End Function
Private Shared Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
'Print transfer progress
_lastProgress = e.FileProgress
End Sub
Public Shared _lastProgress As Integer
...
Further discussion nr 3:
Main form:
Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
End Function)
Dim forma As New FrmProgressBar
forma.ShowDialog()
Progress bar form:
Public Class FrmProgressBar
Public Sub New()
InitializeComponent()
End Sub
Sub Run()
Try
Do
ProgressBar1.Value = WinScpOperation._lastProgress
ProgressBar1.Refresh()
Loop Until ProgressBar1.Value = 1
Cursor = Cursors.Default
Catch ex As Exception
Finally
MsgBox("before sleep")
System.Threading.Thread.Sleep(10000)
MsgBox("after sleep sleep")
Close()
End Try
End Sub
Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Run()
End Sub
End Class
Point nr. 4:
Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
End Function)
Dim pic As New Waiting
pic.ShowDialog()
Task.WaitAll(tsk)
pic.Close()
Point 5:
Dim pic As New Waiting
pic.ShowDialog()
Dim tsk As Task = Task.Factory.StartNew(Sub() WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, pic, True))
Task.WaitAll(tsk)
'pic.Close()
In some other class (maybe didn't mentioned before this method is placed in diffrent class - my custom one)
Public Function GetFile(source As String, destination As String, formclose As InvokeCloseForm, Optional removeSource As Boolean = False) As Boolean
Dim result As Boolean = True
Try
session.GetFiles(source, destination, removeSource).Check()
Catch ex As Exception
result = False
End Try
formclose.RUn()
Return result
End Function
Interface:
Public Interface InvokeCloseForm
Sub RUn()
End Interface
Waiting form :
Public Class Waiting
Implements InvokeCloseForm
Public Sub RUn() Implements InvokeCloseForm.RUn
Me.Close()
End Sub
End Class
The Session.GetFiles method in blocking.
It means it returns only after the transfer finishes.
The solution is to:
Run the WinSCP transfer (the Session.GetFiles) in a separate thread, not to block the GUI thread.
For that see WinForm Application UI Hangs during Long-Running Operation
Handle the Session.FileTransferProgress event.
Though note that the event handler will be called on the background thread, so you cannot update the progress bar directly from the handler. You have to use the Control.Invoke to make sure the progress bar is updated on the GUI thread.
For that see How do I update the GUI from another thread?
A trivial implementation is below.
For a more version of the code, see WinSCP article Displaying FTP/SFTP transfer progress on WinForms ProgressBar.
Public Class ProgressDialog1
Private Sub ProgressDialog1_Load(
sender As Object, e As EventArgs) Handles MyBase.Load
' Run download on a separate thread
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Download))
End Sub
Private Sub Download(stateInfo As Object)
' Setup session options
Dim mySessionOptions As New SessionOptions
With mySessionOptions
...
End With
Using mySession As Session = New Session
AddHandler mySession.FileTransferProgress,
AddressOf SessionFileTransferProgress
' Connect
mySession.Open(mySessionOptions)
mySession.GetFiles(<Source>, <Destination>).Check()
End Using
' Close form (invoked on GUI thread)
Invoke(New Action(Sub() Close()))
End Sub
Private Sub SessionFileTransferProgress(
sender As Object, e As FileTransferProgressEventArgs)
' Update progress bar (on GUI thread)
ProgressBar1.Invoke(
New Action(Of Double)(AddressOf UpdateProgress), e.OverallProgress)
End Sub
Private Sub UpdateProgress(progress As Double)
ProgressBar1.Value = progress * 100
End Sub
End Class
You may want to disable the progress form (or its parts) during the operation, if you want to prevent the user from doing some operations.
Use the .Enabled property of the form or control(s).
Easier, but hacky and generally not recommendable solution, is to call the Application.DoEvents method from your existing SessionFileTransferProgress handler.
And of course, you have to update the progress bar from the the SessionFileTransferProgress as well.
Private Shared Sub SessionFileTransferProgress(
sender As Object, e As FileTransferProgressEventArgs)
'Print transfer progress
ProgressBar1.Value = e.FileProgress
Application.DoEvents
End Sub
And the progress bar's .Minimum and .Maximum must be set before the Session.GetFiles.
But do not do that! That's a wrong approach.
And still, you need to disable the forms/controls the same way as in the correct solution above.

How to call a Sub without knowing which form is loaded into panel?

On every DataGridView1_SelectionChanged event I need to run a Private Sub OnSelectionChanged() of the form that is loaded into Panel1 (see the image http://tinypic.com/r/2nu2wx/8).
Every form that can be loaded into Panel1 has the same Private Sub OnSelectionChanged() that initiates all the necessary calculations. For instance, I can load a form that calculates temperatures or I can load a form that calculates voltages. If different element is selected in the main form’s DataGridView1, either temperatures or voltages should be recalculated.
The problem is - there are many forms that can be loaded into Panel1, and I’m struggling to raise an event that would fire only once and would run the necessary Sub only in the loaded form.
Currently I’m using Shared Event:
'Main form (Form1).
Shared Event event_UpdateLoadedForm(ByVal frm_name As String)
'This is how I load forms into a panel (in this case frm_SCT).
Private Sub mnu_SCT_Click(sender As Object, e As EventArgs) Handles mnu_SCT.Click
frm_SCT.TopLevel = False
frm_SCT.Dock = DockStyle.Fill
Panel1.Controls.Add(frm_SCT)
frm_SCT.Show()
Var._loadedForm = frm_SCT.Name
RaiseEvent event_UpdateLoadedForm(Var._loadedForm)
End Sub
‘Form that is loaded into panel (Form2 or Form3 or Form4...).
Private WithEvents myEvent As New Form1
Private Sub OnEvent(ByVal frm_name As String) Handles myEvent.event_UpdateLoadedForm
‘Avoid executing code for the form that is not loaded.
If frm_name <> Me.Name Then Exit Sub
End Sub
This approach is working but I’m sure it can be done way better (I'd be thankful for any suggestions). I have tried to raise an event in the main form like this:
Public Event MyEvent As EventHandler
Protected Overridable Sub OnChange(e As EventArgs)
RaiseEvent MyEvent(Me, e)
End Sub
Private Sub DataGridView1_SelectionChanged(sender As Object, e As EventArgs) _
Handles DataGridView1.SelectionChanged
OnChange(EventArgs.Empty)
End Sub
but I don't know to subscribe to it in the loaded form.
Thank you.
Taking into account Hans Passant’s comments as well as code he posted in related thread I achieved what I wanted (see the code below).
Public Interface IOnEvent
Sub OnSelectionChange()
End Interface
Public Class Form1
' ???
Private myInterface As IOnEvent = Nothing
' Create and load form.
Private Sub DisplayForm(frm_Name As String)
' Exit if the form is already displayed.
If Panel1.Controls.Count > 0 AndAlso _
Panel1.Controls(0).GetType().Name = frm_Name Then Exit Sub
' Dispose previous form.
Do While Panel1.Controls.Count > 0
Panel1.Controls(0).Dispose()
Loop
' Create form by its full name.
Dim T As Type = Type.GetType("Namespace." & frm_Name)
Dim frm As Form = CType(Activator.CreateInstance(T), Form)
' Load form into the panel.
frm.TopLevel = False
frm.Visible = True
frm.Dock = DockStyle.Fill
Panel1.Controls.Add(frm)
' ???
myInterface = DirectCast(frm, IOnEvent)
End Sub
Private Sub DataGridView1_SelectionChanged(sender As Object, e As EventArgs) _
Handles DataGridView1.SelectionChanged
' Avoid error if the panel is empty.
If myInterface Is Nothing Then Return
' Run subroutine in the loaded form.
myInterface.OnSelectionChange()
End Sub
End Class
One last thing – it would be great if someone could take a quick look at the code (it works) and confirm that it is ok, especially the lines marked with “???” (I don’t understand them yet).

vb.net how do I make an application that only has a notifyicon and no windows form?

I want to make an application that only has a notifyicon and doesn't have any visible window form when it starts up. I see some example sort of like what I want to do for c#, but I don't see how to do this in vb.net project.
A form is not strictly necessary. You can instantiate a NotifyIcon and use that without creating a form:
Public Class AppContext
Inherits ApplicationContext
Private notifyIcon As NotifyIcon
Private appActive As Boolean
Public Sub New()
AddHandler Application.ApplicationExit, AddressOf OnApplicationExit
notifyIcon = New NotifyIcon()
notifyIcon.Icon = My.Resources.ActiveIcon
notifyIcon.Text = "The app is active."
AddHandler notifyIcon.MouseClick, AddressOf OnIconMouseClick
appActive = True
notifyIcon.Visible = True
End Sub
Private Sub OnApplicationExit(ByVal sender As Object, ByVal e As EventArgs)
If notifyIcon IsNot Nothing Then
notifyIcon.Dispose()
End If
End Sub
Private Sub OnIconMouseClick(ByVal sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Left Then
appActive = Not appActive
notifyIcon.Icon = If(appActive, My.Resources.ActiveIcon, My.Resources.InactiveIcon)
notifyIcon.Text = If(appActive, "The app is active.", "The app is not active.")
Else
If MsgBox("Do you want to Exit?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
notifyIcon.Visible = False
ExitThread()
End If
End If
End Sub
End Class
And then start your app from a Sub Main:
Public Module EntryPoint
Public Sub Main()
Dim ctx As New AppContext()
Application.Run(ctx)
End Sub
End Module
Just put the form transparent and resize it to 1x1..
And add a notifyicon..
And on the Form Load Event do this:
NotifyIcon.Visible = True
Then make what ever you want..
You can create a context menu strip (A Menu when you right click on it )
PS: If you do it, You need to go on the NotifyIcon properties and set the Context Menu Strip to that you created..
Hope it helped you..

Errorprovider shows error on using windows close button(X)

Is there any way to turn the damned error provider off when i try to close the form using the windows close button(X). It fires the validation and the user has to fill all the fields before he can close the form..this will be a usability issue because many tend to close the form using the (X) button.
i have placed a button for cancel with causes validation to false and it also fires a validation.
i found someone saying that if you use Form.Close() function validations are run...
how can i get past this annoying feature.
i have a MDI sturucture and show the form using
CreateExam.MdiParent = Me
CreateExam.Show()
on the mdi parent's menuitem click
and have this as set validation
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Any help is much appreciated.
googling only showed results where users were having problem using a command button as close button and that too is causing problem in my case
The ValidateChildren() method prevents the form from closing. Paste this code in your form to fix that:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = false;
}
This is quite simple fix, in your Form's Closing Event, set a flag to indicate leaving the form, for example blnLeave, when the Form gets loaded, set the flag to False, when the Closing event gets triggered, set that to True within that event handler, then the change as follows would be
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If (blnLeave) Then
e.Cancel = False;
Return
End If
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Edit: Amended this answer for inclusion as per OP's comments. My suggestion is to handle the Form's Closed Event as shown
Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
blnLeave = True
End Sub
And handle it here in the Form's window procedure override as shown here....
Private Const SC_CLOSE As Integer = &HF060
Private Const WM_MENUSELECT As Integer = &H11F
Private Function LoWord(ByVal Num As Integer) As Integer
LoWord = Num & &HFFFF
End Function
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_MENUSELECT Then
If LoWord(m.WParam.ToInt32()) = SC_CLOSE Then
' Handle the closing via system Menu
blnLeave = True
End If
End If
MyBase.WndProc(m)
End Sub