I am trying to use the code from the following link:
VB- Helper Create menu items at run time with images, shortcut keys, and event handlers in Visual Basic .NET
The only difference is that I want a local image and not one from my.Recources
What I have is the following:
''Tool 2 displays a string and image.
Dim tool2 As New ToolStripMenuItem("Tool 2", (Image.FromFile("C:\test\icon.jpg")))
tool2.Name = "mnuToolsTool2"
tool2.ShortcutKeys = (Keys.D2 Or Keys.Control) ' Ctrl+2
AddHandler tool2.Click, AddressOf mnuTool2_Click
ToolStripMenuItem1.DropDownItems.Add(tool2)
I could not reproduce this "error". However, from the given text, code and link, my best guess is as follows:
You are using a 64 bit machine.
You run the code inside the Form.Load event.
An error occurs somewhere in this method.
Private Sub _Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Code...
Throw New Exception("ooops..")
'Code...
End Sub
As you might not know is that errors thrown in the Form.Load on a 64 bit machine are "swallowed" by the system.
For more information, read this SO post: Why the form load can't catch exception?
You should move your code inside the constructor:
Public Sub New()
Me.InitializeComponent()
'Code goes here...
End Sub
Or change to the Form.Shown event:
Private Sub _Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
Try
'Code goes here...
Catch ex As Exception
MessageBox.Show(ex.Message, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Related
Ref: There are 20ish panels that need this event at visibility change, all with the same naming conventions being project_x (x being the panel number)
I'm not able to do an expectation vs reality on this code because I was not able to find how to best do this.
Further Explanation: On the event 'VisibleChanged' Which is controlled by an External Class, We want a way to apply this dynamically in a shorter code.
Sure we can do this and hard code everything in and it works fine, but I was wondering if there was a way to do this dynamically.
Private Sub project_1_VisibleChanged(sender As Object, e As EventArgs) Handles project_1.VisibleChanged
Try
If project_1.Visible = True Then
'There's code here but it's been omitted
End If
Catch ex As Exception
End Try
End Sub
You can find all relevant panels in form load and subscribe each to a single event handler method
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
'assuming the panels are on the main form you would use Me
'else you would use the name of the control that the panel is located within - e.g. SomeControlName.Controls...
Dim panels = Me.Controls.OfType(Of Panel).Where(Function(panel) panel.Name.StartsWith("project_"))
For Each panel In panels
AddHandler panel.VisibleChanged, AddressOf PanelVisibilityChanged
Next
End Sub
Private Sub PanelVisibilityChanged(sender As Object, e As EventArgs)
Dim panel = DirectCast(sender, Panel)
Try
If panel.Visible Then
'assuming the picture box is within the panel
Dim pictureBox = panel.Controls.Find($"{panel.Name}_img", True)(0)
'There's code here but it's been omitted
End If
Catch ex As Exception
End Try
End Sub
Yes, like this:
Private Sub panels_VisibleChanged(sender As Object, e As EventArgs) _
Handles project_1.VisibleChanged,
project_2.VisibleChanged,
project_3.VisibleChanged,
project_4.VisibleChanged,
project_5.VisibleChanged,
project_6.VisibleChanged,
project_7.VisibleChanged
Dim ctrl As Panel = DirectCast(sender, Panel)
Try
If ctrl.Visible = True Then
'There's code here but it's been omitted
End If
Catch ex As Exception
End Try
End Sub
There are a couple of other ways to do this as well. This is probably the cleanest for VB.net and your situation.
I have a form used to display options about processes.
When options are applyed :
frmOptions
For Each ltvi As ListViewItem In ltvProcesses.CheckedItems
Dim proc As Process = CType(ltvi.Tag, Process)
targeted_processes.Add(proc)
AddHandler proc.Exited, AddressOf frmAET.a_target_process_has_been_exited
proc.EnableRaisingEvents = True
Next
And in a tools module :
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
frmAET.btnStatus.ForeColor = Color.Red
msgbox("OK")
End Sub
And... the messagebox displays its message but the color doesn't change.
After some tries, the problem is when a_target_process_has_been_exited is actived by the handler.
If I do this (Button1 belongs to frmAET, like btnStatus) :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
a_target_process_has_been_exited()
End Sub
It works ! But not when I really want (when a process is ended).
So, the problem is when the sub is called by the process end event.
And when I try to specify this (maybe a frmAET's sub can modify its controls) :
AddHandler leproc.Exited, AddressOf frmAET.a_target_process_has_been_exited
Error : Reference to a non-shared member requires an objet reference
Could you help me ?
Your AddHandler seems to use AddressOf frmAET.a_target_process_has_been_exited, that means method in frmAET form itself. Not tools module as you stated.
Let's consider your frmOptions is correct and frmAET is containing this (with removed explicit reference to frmAET, since it's local)
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
btnStatus.ForeColor = Color.Red
MsgBox("OK")
End Sub
As comments already explained, your event handler is called in another thread and you need to sync yourself to main UI thread. For example like this:
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
Me.BeginInvoke(Sub() HandleProcessExit())
End Sub
Public Sub HandleProcessExit
btnStatus.ForeColor = Color.Red
MsgBox("OK")
End Sub
This version will block main UI thread until you click on the MsgBox button.
You should add some Try/Catch block. Exception in another threads are difficult to detect otherwise.
This code depends on implicit form instances that VB.NET creates for you. I expect your frmAET is actually My.Forms.frmAET instance to make this work.
I have a Telerik Tabbed PageView in my Windows Form application which I can edit tab's titles by double clicking on them which initiates a text editor. Following subs are taking care of altered labels - check the edited labels not to be blank:
Private Sub ViewElement_EditorInitialized(sender As Object, e As UI.RadPageViewEditorEventArgs)
AddHandler MountingSystemTabControl.ViewElement.ActiveEditor.Validating, AddressOf ActiveEditor_Validating
AddHandler MountingSystemTabControl.ViewElement.ActiveEditor.Validated, AddressOf ActiveEditor_Validated
AddHandler MountingSystemTabControl.ViewElement.ActiveEditor.ValidationError, AddressOf ActiveEditor_ValidationError
End Sub
Private Sub ActiveEditor_Validating(sender As Object, e As CancelEventArgs)
Dim editor As UI.RadPageViewElement.PageViewItemTextEditor = TryCast(sender, UI.RadPageViewElement.PageViewItemTextEditor)
If editor IsNot Nothing AndAlso MountingSystemTabControl.ViewElement.ActiveEditor.Value = String.Empty Then
e.Cancel = True
End If
End Sub
Private Sub ActiveEditor_ValidationError(sender As Object, e As UI.ValidationErrorEventArgs)
RadMessageBox.Show("Array label can't be empty!", "Error", MessageBoxButtons.OK, RadMessageIcon.[Error])
End Sub
Private Sub ActiveEditor_Validated(sender As Object, e As EventArgs)
RadMessageBox.Show("Array label has been successfully updated!", "Information", MessageBoxButtons.OK, RadMessageIcon.Info)
End Sub
Moreover, this line is there in my Form_Load event:
AddHandler MountingSystemTabControl.ViewElement.EditorInitialized, AddressOf ViewElement_EditorInitialized
Now the problem is, whenever I run the code, after the MessageBox shows me the "Array label has been successfully updated!" message, a NullReference Exception is thrown which it seems I can't catch it not even with Application Event handlers ! After breaking the code, Visual Studio refers me to this line as the source of the exception:
RadMessageBox.Show("Array label has been successfully updated!", "Information", MessageBoxButtons.OK, RadMessageIcon.Info)
And that's what confuses me because I can't find anything referenced in that line (of course there is something but I don't know what).
Call Stack screenshot
Main idea of the code.
As memory serves...I think RadMessageBox calls for a Parent argument.
Try this:
RadMessageBox.Show(Me, "Array label has been successfully updated!", "Information", MessageBoxButtons.OK, RadMessageIcon.Info)
The issue was addressed by updating to 'Telerik UI for WinForms Q1 2015 (version 2015.1.225)'
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.
I'm working on an application to read something from a serial port (COMM-port).
In short, it works like this: when you work in a bar or restaurant, before you can enter something in the register, you have to scan a sort of card. If this card returns a good number, you can enter something.
So, there has to be a form that listens to the serial port and checks whether someone scans a card and if it's a card with good rights.
If the person has the good rights, the form can be closed and another form is called.
Now, in code:
Here, the MenuForm is loaded (the form that has to be accesible after the correct code was read). I call the frmWaiterKey to show up.
Private Sub frmMenu_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim oForm As frmWaiterKey = New frmWaiterKey()
oForm.ShowDialog()
End Sub
The code of the class frmWaiterKey:
Private Sub frmWaiterKey_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
nameArray = SerialPort.GetPortNames
OpenComPort()
AddHandler myComPort.DataReceived, SerialDataReceivedEventHandler1
End Sub
Sub OpenComPort()
Try
' Get the selected COM port's name
' from the combo box.
If Not myComPort.IsOpen Then
myComPort.PortName = _
nameArray(0).ToString()
' Get the selected bit rate from the combo box.
myComPort.BaudRate = CInt(9600)
' Set other port parameters.
myComPort.Parity = Parity.None
myComPort.DataBits = 8
myComPort.StopBits = StopBits.One
myComPort.Handshake = Handshake.None
'myComPort.ReadTimeout = 3000
'myComPort.WriteTimeout = 5000
' Open the port.
myComPort.Open()
End If
Catch ex As InvalidOperationException
MessageBox.Show(ex.Message)
Catch ex As UnauthorizedAccessException
MessageBox.Show(ex.Message)
Catch ex As System.IO.IOException
MessageBox.Show(ex.Message)
End Try
End Sub
Sub CloseComPort()
Using myComPort
If (Not (myComPort Is Nothing)) Then
' The COM port exists.
If myComPort.IsOpen Then
' Wait for the transmit buffer to empty.
Do While (myComPort.BytesToWrite > 0)
Loop
End If
End If
End Using
End Sub
Private SerialDataReceivedEventHandler1 As New SerialDataReceivedEventHandler(AddressOf DataReceived)
' Specify the routine that runs when
' a DataReceived event occurs at myComPort.
' This routine runs when data arrives at myComPort.
Friend Sub DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
Dim newReceivedData As String
' Get data from the COM port.
newReceivedData = myComPort.ReadExisting
newReceivedData = newReceivedData.Trim()
MsgBox(newReceivedData)
If newReceivedData.Equals("00150324294764") Then
CloseComPort()
Me.Close()
End If
End Sub
I get an error in the last line: Me.Close()
I get the point: I call the form frmWaiterKey from the frmMenu and can't close it here...
But I have no idea how to solve this problem.
I hope someone can help me or tell me what I'm doing wrong.
First, you need to make a method like this:
Private Sub CloseMe()
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf CloseMe))
Exit Sub
End If
Me.Close()
End Sub
Then, close your form by calling that method, like this:
If newReceivedData.Equals("00150324294764") Then
CloseComPort()
CloseMe()
End If
The reason this is necessary is because all UI activity in WinForms must be performed from the same thread. Since the DataReceived method is being called from another thread, it must get back onto the UI thread before it can close the form. The InvokeRequired property returns true if you are on any thread other than the UI thread, and the Invoke method invokes the given method from the UI thread.