Parent form sometimes does not close (Only in Windows 10) - vb.net

My main form is frmInvoice. This sub is located inside frmInvoice.
This is one of the Subs that sometimes causes frmDark to not close. frmLookup does not display when this happens. frmDark just stays there covering frmInvoice. It's like it doesn't reach the call to frm.ShowDialog(frmDark), cause when I press the lookup key, it displays the frmLookup, but upon closing frmLookup, frmDark is still there.
No exception is being raised.
Note that this only happens in Windows 10. In Windows 8/7, this never happened. What am I missing?
This happens at different times. Sometimes I could press the lookup key for 20 times and it will display fine. Sometimes, after 1 press of the lookup key and this happens.
Private Sub ItemLookup()
Try
Using frmDark As New Form
With frmDark
.ShowInTaskbar = False
.Icon = Me.Icon
.FormBorderStyle = Windows.Forms.FormBorderStyle.None
.BackColor = Color.Black
.Opacity = 0.95
.WindowState = FormWindowState.Maximized
.Show(Me)
Using frm As New frmLookup
With frm
.Icon = Me.Icon
.ShowDialog(frmDark)
frmDark.Close()
If .DialogResult = Windows.Forms.DialogResult.OK Then
' Do stuff here
End If
End With
End Using
End With
End Using
Catch ex As Exception
ErrMsg(ex)
End Try
End Sub
UPDATE: I'm using .Net Framework 4.8
Thanks

I would suggest rearranging the code like so:
Dim lookupResult As DialogResult
Using frmDark As New Form With {.ShowInTaskbar = False,
.Icon = Me.Icon,
...}
frmDark.Show(Me)
Using frm As New frmLookup With {.Icon = Me.Icon}
lookupResult = frm.ShowDialog(frmDark)
End Using
End Using
If lookupResult = DialogResult.OK Then
'...
End If
Because that code exits the Using block that created frmDark, there should be no way that it can't close.
Also, instead of using a vanilla Form and configuring it on demand, I would suggest that you create a dedicated form type to use as the overlay in that scenario. You can then get rid of all the property assignments.
Having a dedicated overlay form would also allow you to reconfigure things significantly and, in my opinion, better. The overlay form could have a property of type Form. You main form could then create a frmLookup instance and assign it to that property, than call ShowDialog on the overlay form. In the Shown event handler of the overlay form, it could then call ShowDialog on the form in that property. When that call returns, it could assign the result to its own DialogResult property and close itself. The main form would then just get the result from calling ShowDialog on the overlay. That might look like this:
Public Class OverlayForm
Public Property DialogueForm As Form
Private Sub OverlayForm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
DialogResult = DialogueForm.ShowDialog()
End Sub
End Class
and this:
Public Class MainForm
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Using dialogue As New DialogueForm,
overlay As New OverlayForm With {.DialogueForm = dialogue}
If overlay.ShowDialog() = DialogResult.OK Then
MessageBox.Show("OK")
End If
End Using
End Sub
End Class

Related

Form Enabled in FormClosing Event Not Working

I'll show you my code first:
Private Sub AddProductToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AddProductToolStripMenuItem.Click
Me.Enabled = False
Dim frmAddProduct As New FormAddProduct
frmAddProduct.Show()
frmAddProduct.Owner = Me
End Sub
That is my Main Form to call AddProduct form, and this is my FormClosing in AddProduct
Private Sub FormAddProduct_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
ButtonReset.PerformClick()
Lock()
Me.Owner = Nothing
Me.Hide()
Dim frmMainIndex As New FormMainIndex
frmMainIndex.Enabled = True
End Sub
So I have set enabled = false in my main form when it call Add Product form, and enabled = true when I close my Add Product form, but enabled = true won't work.
When I close my Add Product, it's only hide Add Product form but not enabling main form, main form still not enabled. Is there something wrong with my code?
This line is your problem:
Dim frmMainIndex As New FormMainIndex
You are instantiating a new FormMainIndex. Whenever you use the New keyword you are creating a completely new and independant object. frmMainIndex is a completely different form than the first one which opened your FormAddProduct form.
Since you've set the FormAddProduct's owner to your FormMainIndex form, just set the owner's Enabled property to True instead:
ButtonReset.PerformClick()
Lock()
Me.Owner.Enabled = True
Me.Owner = Nothing
Me.Hide()
Also, your Me.Hide() call doesn't make any sense since your form is about to be closed.
Because you created a new instance of FormMainIndex, the frmMainIndex.Enabled was applied to this new instance, not the one that created your FormAddProduct. Why don't you show your FormAddProduct as a dialog. like this
Dim frmAddProduct As New FormAddProduct
frmAddProduct.ShowDialog(Me)

Winforms - MDI Parent refresh/re-activate

I have a MDI Parent form which has a menustrip for the application. My application startup file is the MDI Parent form which on load calls a child login form. Code as below:
Dim myForm As Form = New Login
Dim formResult As DialogResult = myForm.ShowDialog()
If formResult = Windows.Forms.DialogResult.OK Then
If LoginSucceeded = True Then
Me.tabMainMenu.Visible = True
ApplyUserAccess(eApp.DataAccess.DAL_UserSettings.SelectMenuSettingByUserID(glbUserID))
myForm.Dispose()
End If
End If
The menustrip has a Logout label which when clicked disables the menu strip and displays the login form again.
The boolean field LoginSucceeded determines a successful validation of the user credentials and sets the menu according to the access given to that user. My problem is the first time the main menu on the MDI parent is set properly based on the user's access. After logging out and logging in again, i wanted to set the main menu accordingly again which is not happening.
The Form_Load event on the MDI Parent is being executed only once.
Any tips of re-painting the MDI parent when it receives focus the 2nd time onwards.
Thanks,
ZK
My code for the Logoff is as below:
Dim blnLogout As DialogResult = MessageBox.Show("Are You Sure You Want To Logout?", "eApp", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If blnLogout = Windows.Forms.DialogResult.Yes Then
SetToolbarMenuStyle()
tabMainMenu.Visible = False
LoginSucceeded = False
blnShowLoginTab = True
Dim myForm As Form = New Login
myForm.MdiParent = Me
myForm.WindowState = FormWindowState.Normal
myForm.Show()
End If
Move your login code to its own method in your main form so you can call it multiple times:
Public Class Form1
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown ValidateLogin()
ValidateLogin()
End Sub
Private Sub LoginLogoutToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles LoginLogoutToolStripMenuItem.Click
ValidateLogin()
End Sub
Private Sub ValidateLogin()
' disable appropriate main form elements so they can't access anything:
Me.tabMainMenu.Visible = False
Using myForm As New Login
If myForm.ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
' login succeeded: re-enable main form elements
Me.tabMainMenu.Visible = True
ApplyUserAccess(eApp.DataAccess.DAL_UserSettings.SelectMenuSettingByUserID(glbUserID))
Else
MessageBox.Show("Login Failed")
End If
End Using
End Sub
End Class
You also don't need the "LoginSucceeded" variable. You can pass a success/failure back to the main form by setting DialogResult to OK in your Login form:
Public Class Login
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If True Then ' <-- perform your check
Me.DialogResult = Windows.Forms.DialogResult.OK ' only return OK if login has succeeded
End If
End Sub
End Class
Here are presumptions on your code, I'm guessing you added the code about on the Form.Load event. The Form.Load event only gets raised when the form is shown for the first time.
According to MSDN
Form.Load Event
Occurs before a form is displayed for the first time.
And now, when you Log-Off, you're setting the visibility of the form to false. So what I suggest is you move your code from the Form.Load event to the Form.VisibleChanged event.
According to MSDN
Form.Load Event
Occurs when the Visible property value changes.

Closing form with Gif throws InvalidOperationException

This is clearly a problem of me not understanding how to properly setup a UI thread, but I can't figure out how to fix it.
I have a datagridview where I click a button, get the information from the network, and then display it on the datagridview with the new data. While it is on the network I have a form I show with an updating gif, a form I called "loading". Within that form I have the gif updating using the typical OnFrameChanged and m_isAnimating code that is on the internet.
However, no matter what format I use, I always get this exception caught here:
Public loader As New Loading
Private Sub OnFrameChanged(ByVal o As Object, ByVal e As EventArgs)
Try ' If animation is allowed call the ImageAnimator UpdateFrames method
' to show the next frame in the animation.
Me.Invalidate()
If m_IsAnimating Then
ImageAnimator.UpdateFrames()
Me.Refresh()
'Draw the next frame in the animation.
Dim aGraphics As Graphics = PictureBox1.CreateGraphics
aGraphics.DrawImage(_AnimatedGif, New Point(0, 0))
aGraphics.Dispose()
End If
Catch ex As InvalidOperationException
End Try
End Sub
And it usually says something along the lines of "was accessed from a thread it wasn't created on" or "Cannot access a disposed object. Object name: 'PictureBox'."
But I don't know why that is, since I am creating a new instance here every time. Here's the button's code:
Private Sub btnSlowSearch_Click(sender As Object, e As EventArgs) Handles btnSlowSearch.Click
Me.Cursor = Cursors.WaitCursor
'get datatable
loader.Show()
BWorkerLoadProp.RunWorkerAsync() 'go get data on network
'bworker will update datagridview with new data
'wait for worker to finish
If BWorkerLoadProp.IsBusy Then
Threading.Thread.Sleep(1)
End If
loader.Close()
End Sub
I realize it isn't very good code, but I have tried putting the loader inside the background worker, I have tried whatever. But no matter what the exception is called.
What's the proper way to show another updating form as I do background work?
The behavior documented is difficult to reproduce.
Probably something between the thread switching causes a call to OnFrameChanged after the call to close in the btnSlowSearch_Click.
In any case logic seems to suggest to call the ImageAnimator.StopAnimate in the close event of the form that shows the animation
So looking at your comment above I would add the following to your animator form
// Not needed
// Public loader As New Loading
Private Sub OnFrameChanged(ByVal o As Object, ByVal e As EventArgs)
Try
Me.Invalidate()
If m_IsAnimating Then
ImageAnimator.UpdateFrames()
Me.Refresh()
'Draw the next frame in the animation.
Dim aGraphics As Graphics = PictureBox1.CreateGraphics
aGraphics.DrawImage(_AnimatedGif, New Point(0, 0))
aGraphics.Dispose()
End If
Catch ex As InvalidOperationException
.. do not leave this empty or remove altogether
End Try
End Sub
Private Sub Form_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
... if you need to stop the closing you should do it here without stopping the animation
If m_IsAnimating Then
ImageAnimator.StopAnimate(AnimatedGif, _
New EventHandler(AddressOf Me.OnFrameChanged))
m_isAnimating = False
End If
End Sub
This is certainly not the only way to do this but I will provide you the simplest working example in hopes that it will help you to correct your own application.
1) Create a new vb.net windows forms application and add a button (Button1) onto the form.
2) Change the Form1 code to this:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If fLoading Is Nothing Then ' can only show one loading screen at a time
Dim oLoadingThread As clsLoadingThread = New clsLoadingThread ' creat new thread
oLoadingThread.ShowWaitScreen() ' show the loading screen
'-----------------------------------------
' your real processing would go here
'-----------------------------------------
For i As Int32 = 0 To 999999
Application.DoEvents()
Next
'-----------------------------------------
oLoadingThread.CloseLoadingScreen() ' we are done processing so close the loading form
oLoadingThread = Nothing ' clear thread variable
End If
End Sub
End Class
Public Class clsLoadingThread
Dim oThread As System.Threading.Thread
Private Delegate Sub CloseLoadingScreenDelegate()
Public Sub ShowWaitScreen()
' create new thread that will open the loading form to ensure animation doesn't pause or stop
oThread = New System.Threading.Thread(AddressOf ShowLoadingForm)
oThread.Start()
End Sub
Private Sub ShowLoadingForm()
Dim fLoading As New frmLoading
fLoading.ShowDialog() ' Show loading form
If fLoading IsNot Nothing Then fLoading.Dispose() : fLoading = Nothing ' loading form should be closed by this point but dispose of it just in case
End Sub
Public Sub CloseLoadingScreen()
If fLoading.InvokeRequired Then
' Since the loading form was created on a seperate thread we need to invoke the thread that created it
fLoading.Invoke(New CloseLoadingScreenDelegate(AddressOf CloseLoadingScreen))
Else
' Now we can close the form
fLoading.Close()
End If
End Sub
End Class
Module Module1
Public fLoading As frmLoading
End Module
3) Add a new form and call it frmLoading. Add a picturebox to the form and set the image to your updating gif.
4) Change the frmLoading code to this:
Public Class frmLoading
Private Sub frmLoading_Load(sender As Object, e As EventArgs) Handles Me.Load
fLoading = Me ' ensure that the global loading form variable is set here so we can use it later
End Sub
Private Sub frmLoading_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
fLoading = Nothing ' clear the global loading form since the form is being disposed
End Sub
End Class
Normally I would add the clsLoadingThread Class and Module1 Module to their own files but it's easier to show the code to you this way.

Show single instance of form in other project

I have multiproject solution where one project is "main", startup project.
From this project I starting forms in other projects which all are referenced in startup project.
Problem is that I can start those forms as instances like this:
Dim ka As New otherproject.frm_thatform
With ka
.BringToFront()
.Show(Me)
End With
That opens new "thatform" every time but I would like to to start "thatform" like single instance every time and that "thatform" comes to front.
How to do that?
I try like this:
Dim ka As otherproject.frm_thatform
With ka
.BringToFront()
.Show(Me)
End With
... but that don't work (Object reference not set to an instance of an object).
The trick is to not Dim the form and use it as a Shared object.
Otherproject.frm_thatform.Show()
Otherproject.frm_thatform.BringToFront()
As long as you dont close this window you can call it the same way as a Dimmed object.
Instead of ka you just type Otherproject.frm_thatform
As soon as you close the window it will loose everything in it.
EDIT: Apparently this only works with forms inside the project. My bad :(
What you need to do instead is keep a list of Forms inside your main project.
Make sure you give the form a name, and when you click the button to open the form simply loop through the list to find the set name.
Something like this:
Private FormList As New List(Of Form)
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim theForm As Form = Nothing
For Each f As Form In FormList
If f.Name = "Selected form name" Then
theForm = f
End If
Next
If theForm Is Nothing Then
theForm = New Otherproject.frm_thatform()
theForm.Name = "Selected form name"
FormList.Add(theForm)
End If
End Sub
A couple of options -
create a static/ shared form variable
Shared form As SingletonformEx.Form1
If form IsNot Nothing Then
form.BringToFront()
Else
form = New SingletonformEx.Form1()
form.Show()
End If
Extend the form class and implement singleton pattern on it.
Public Class SingletonForm Inherits Form
Private Shared m_instance As SingletonForm
Private Sub New()
'InitializeComponent();
End Sub
Public Shared ReadOnly Property Instance() As SingletonForm
Get
If m_instance Is Nothing Then
m_instance = New SingletonForm()
End If
m_instance.BringToFront()
Return m_instance
End Get
End Property
End Class
note: this is C# code converted to vb.net so i am not sure if this is perfect
In your original code, move the form declaration out to class (form) level:
Private ka As otherproject.frm_thatform = Nothing
Then check to see if it is Nothing or has been disposed, and recreate it as necessary:
If (ka Is Nothing) OrElse ka.IsDisposed Then
ka = New otherproject.frm_thatform
ka.Show(Me)
End If
If ka.WindowState = FormWindowState.Minimized Then
ka.WindowState = FormWindowState.Normal
End If
ka.BringToFront()

Remove the windows form controls on exit

I'm adding the form controls on loading the form manually:
Me.FieldI = New TextBox()
Me.FieldI.Location = New System.Drawing.Point(50, 10)
Me.FieldI.Name = "FieldI"
Me.FieldI.Size = New System.Drawing.Size(40, 20)
Me.FieldI.TabIndex = 5
Me.Conversion.Controls.Add(Me.FieldI)
[..]
When I close the form window and reopen it, the control is still there (with the old .Text content , because its an textbox in this case).
I would like to remove the controls that have been created while form loading on the form close event, to prevent doubling the elements on my form.
How can I achieve this?
edit
Form closing code looks following (just showing up the main form back):
Private Sub Form1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.FormClosing
Main.Show()
End Sub
The problem here is that the form is not being disposed, so when you open it again the controls are still there from the last time it was opened.
Try the following:
Using frm = New subForm()
frm.ShowDialog()
End Using
The variable frm will be disposed after the using.
Also...
You can also provide feedback from a dialog, to check whether the form was successful or not. For example:
Dim frm As New subForm()
If frm.ShowDialog = DialogResult.OK Then
'YAY!
Else
'Something failed
End If