I have 3 check boxes that I want to act as a radial button group, but my new job is in vb.net and WinForms.
My hope was to simplify the 3 event handlers by replacing the commented code with a function call like this:
Private Sub cbLinqQuery_CheckedChanged(sender As Object, e As EventArgs) handles cbLinqQuery.CheckedChanged
SafeCheckChg(cbDataTable, cbDataTable_CheckedChanged, cbLinqQuery.Checked)
'RemoveHandler cbDataTable.CheckedChanged, AddressOf cbDataTable_CheckedChanged
'cbDataTable.Checked = Not cbLinqQuery.Checked
'AddHandler cbDataTable.CheckedChanged, AddressOf cbDataTable_CheckedChanged
RemoveHandler cbArray.CheckedChanged, AddressOf cbArray_CheckedChanged
cbArray.Checked = Not cbLinqQuery.Checked
AddHandler cbArray.CheckedChanged, AddressOf cbArray_CheckedChanged
btnDefaultBoundData_Click(sender, e)
End Sub
Private Sub SafeCheckChg(ByRef cb As CheckBox, ByRef handler As EventHandler, checked As Boolean)
RemoveHandler cb.CheckedChanged, AddressOf handler
cb.Checked = Not checked
AddHandler cb.CheckedChanged, AddressOf handler.Clone
End Sub
But I've had no success passing the event handler as a parameter. I've tried passing it as an event handler, a delegate and a void but using it in the RemoveHandler/AddressOf call doesn't seem to be understood by the compiler.
Is there a method where I can get the event handler off of the passed object? Is there a type I can use as the parameter. Obviously I'm not desperate, this is in the name of better code not better functionality.
So Found a pretty close answer that worked with a small tweak:
Inspiration
Solution was simply to pass AddressOf foo as the parameter and remove the AddressOf from the RemoveHandler / AddHandler call. (Which for some reason I thought was a required part of the Add/RemoveHandler call and not a separate function call operating on a parameter.)
Final Code:
Private Sub cbLinqQuery_CheckedChanged(sender As Object, e As EventArgs) Handles cbLinqQuery.CheckedChanged
SafeCheckChg(cbDataTable, AddressOf cbDataTable_CheckedChanged, cbLinqQuery.Checked)
SafeCheckChg(cbArray, AddressOf cbArray_CheckedChanged, cbLinqQuery.Checked)
btnDefaultBoundData_Click(sender, e)
End Sub
Private Sub SafeCheckChg(ByRef cb As CheckBox, ByRef handler As EventHandler, checked As Boolean)
RemoveHandler cb.CheckedChanged, handler
cb.Checked = checked
AddHandler cb.CheckedChanged, AddressOf handler.Clone
End Sub
Related
I had difficulty deleting an event
, I only call contextmenu temporarily (assigning the event and after the event has finished I no longer use it), and so on for each call.
sub register()
Dim f_Cm As Windows.Forms.ContextMenuStrip = New System.Windows.Forms.ContextMenuStrip(Me.components)
AddHandler f_Cm.Closed, Sub() f_Cm_Closed(f_Cm)
end sub
'mycode1
Private Sub f_Cm_Closed(f_Cm As Windows.Forms.ContextMenuStrip)
'....mycode
RemoveHandler f_Cm.Closed, Sub() f_Cm_Closed(f_Cm)
End Sub
'mycode2
Private Sub f_Cm_Closed(f_Cm As Windows.Forms.ContextMenuStrip)
'....mycode
Dim e1 As ToolStripDropDownClosedEventArgs = address of f_Cm_Closed(f_Cm)
RemoveHandler f_Cm.Closed, e1
End Sub
Do I need to delete them in this case? and how to do this?
Thanks you!
Sub() f_Cm_Closed(f_Cm) is what's called a lambda expression. Lambda expression are basically methods without a name; they're useful shortcuts in some situations. What you are doing in the code Sub() f_Cm_Closed(f_Cm) is creating a new, nameless method, which then calls f_Cm_Closed(f_Cm).
This isn't what you want, you want to pass a reference directly to your handler so you can remove it later. For that, you use AddressOf.
Before you can do that, the method signatures will have to match. So
Private Sub f_Cm_Closed(f_Cm As Windows.Forms.ContextMenuStrip)
will have to become
Private Sub f_Cm_Closed(sender As Object, e As ToolStripDropDownClosedEventArgs)
sender will always be f_Cm, so you can cast like so:
Dim f_Cm As Windows.Forms.ContextMenuStrip = sender
To pull everything together, your AddHandler call now becomes:
AddHandler f_Cm.Closed, AddressOf f_Cm_Closed
And your method f_Cm_Closed becomes:
Private Sub f_Cm_Closed(sender As Object, e As ToolStripDropDownClosedEventArgs)
Dim f_Cm As Windows.Forms.ContextMenuStrip = sender
RemoveHandler f_Cm.Closed, AddressOf f_Cm_Closed
End Sub
As a final thought, I have no idea why you would want to remove the handler for the Closed event after the menu is closed. But this is how you would restructure your code to do it.
I have a problem with a code I written in VB.NET that use the AddHandler/RemoveHandler token.
This is the code that I have written:
Private Sub Remove_Handler()
RemoveHandler txtSearch.TextChanged, AddressOf Ricarica_elenco
RemoveHandler cbAttori.SelectedIndexChanged, AddressOf Ricarica_elenco
RemoveHandler cbGeneri.SelectedIndexChanged, AddressOf Ricarica_elenco
RemoveHandler cb_plex.CheckStateChanged, AddressOf Ricarica_elenco
End Sub
Private Sub Add_Handler()
AddHandler txtSearch.TextChanged, AddressOf Ricarica_elenco
AddHandler cbAttori.SelectedIndexChanged, AddressOf Ricarica_elenco
AddHandler cbGeneri.SelectedIndexChanged, AddressOf Ricarica_elenco
AddHandler cb_plex.CheckStateChanged, AddressOf Ricarica_elenco
End Sub
Private Sub Ricarica_elenco(sender As Object, e As EventArgs)
CreaElenco()
End Sub
I have 4 events that handles one subroutine. After executing the "Add_Handler" sub, When two or more indicated events occur simultaneously, the subroutine "Ricarica_Elenco" is executed several times.
I need to create a custom event (or someelse) that collects the 4 original events and executes the procedure "Ricarica_Elenco" only once if two or more events occur simultaneously.
How can I do it?
Thanks
Marcello
Events don't occur simultaneously. Probably inside the CreaElenco method you are doing something that triggers another call to CreaElenco like modifying the text of txtSearch or modifying the current index of cbAttori
This is enough to trigger the call to the associated event handler and in cascade a call to CreaElenco while you are still processing the first call to CreaElenco. A simple solution is to remove the handlers before entering the CreaElenco method and readding them when you have finished. Of course you should be sure to always readd the handlers whatever happens inside the CreaElenco method. This could be accomplished adding a Try/Finally clause around the code that removes the handlers.
Private Sub Ricarica_elenco(sender As Object, e As EventArgs)
Try
Remove_Handler()
CreaElenco()
Finally
Add_Handler()
End Try
End Sub
One way to go about this is to create a static boolean variable which indicate whether the method is running or not:
Private Sub Ricarica_elenco(sender As Object, e As EventArgs)
Static currentlyRunning As Boolean
If currentlyRunning Then Exit Sub
currentlyRunning = True
CreaElenco()
currentlyRunning = False
End Sub
You can also replace the static variable with a class-level variable if you prefer.
I have a function GetLogs that calls another function GetHistoricalData. GetHistoricalData is running in a thread and at the end of it, it fires an event AsyncLoggingRequestFinished. What I want is to wait in GetLogs for the event to be raised. Here is a code example to make thinks clearer:
Private Sub GetLogs
_historicalLogging = new HistoricalLogging()
AddHandler _historicalLogging.AsyncLoggingRequestFinished, AddressOf _historicalLogging_AsyncLoggingRequestFinished
_historicalLogging.GetHistoricalData("LogCategory")
'I want to wait here and after the event raised, keep going
End Sub
Private Sub _historicalLogging_AsyncLoggingRequestFinished(ByVal sender As Object, ByVal e As AsyncRequestSuccessArgs)
RemoveHandler _historicalLogging.AsyncLoggingRequestFinished, AddressOf _historicalLogging_AsyncLoggingRequestFinished
For Each entry in _historicalLogging.Entries
'DoSomething
Next
End Sub
Note: HistoricalLogging is an object from a DLL. I cant change any code of that object
Not the best way to do it but it works
Private Sub GetLogs()
_historicalLogging = New HistoricalLogging()
AddHandler _historicalLogging.AsyncLoggingRequestFinished, AddressOf _historicalLogging_AsyncLoggingRequestFinished
_historicalLogging.GetHistoricalData("LogCategory")
'I want to wait here and after the event raised, keep going
Do Until EventRaisen
Application.DoEvents()
Loop
EventRaisen = False
'Code After The Event Has Raisen
End Sub
Dim EventRaisen As Boolean = False
Private Sub _historicalLogging_AsyncLoggingRequestFinished(ByVal sender As Object, ByVal e As AsyncRequestSuccessArgs)
RemoveHandler _historicalLogging.AsyncLoggingRequestFinished, AddressOf _historicalLogging_AsyncLoggingRequestFinished
For Each entry In _historicalLogging.Entries
EventRaisen = True
'DoSomething
Next
End Sub
i am Handling Event in vb.net
AddHandler ButtonOne.Click, AddressOf ButtonClick
Private Sub ButtonClick(ByVal ctrl As Office.CommandBarButton, ByRef Cancel As Boolean)
//do stuff
End sub
But my event calling twice.when i click onces on button and in another method also
i am handling same event
Private Sub AddToolbar()
// do something
AddHandler firstButton.Click, AddressOf ButtonClick
End
How to resovle this issue ?
plz help me
As Hans pointed out.... you are calling the button click twice... Also see below
AddHandler ButtonOne.Click, AddressOf ButtonClick 'You dont really need this if you add a handler to the sub see below...
Private Sub ButtonClick(ByVal ctrl As Office.CommandBarButton, ByRef Cancel As Boolean) Handles ButtonOne.Click
//do stuff
End sub
I have a form that has a start button (to allow users to run the processes over and over if they wish), and I want to send a btnStart.Click event when the form loads, so that the processes start automatically.
I have the following function for the btnStart.Click event, but how do I actually tell Visual Basic 'Pretend someone has clicked the button and fire this event'?
I've tried going very simple, which essentially works. However, Visual Studio gives me a warning Variable 'sender' is used before it has been assigned a value, so I'm guessing this is not really the way to do it:
Dim sender As Object
btnStart_Click(sender, New EventArgs())
I have also tried using RaiseEvent btnStart.Click, but that gives the following error:
'btnStart' is not an event of 'MyProject.MyFormClass
Code
Imports System.ComponentModel
Partial Public Class frmProgress
Private bw As BackgroundWorker = New BackgroundWorker
Public Sub New()
InitializeComponent()
' Set up the BackgroundWorker
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
' Fire the 'btnStart.click' event when the form loads
Dim sender As Object
btnStart_Click(sender, New EventArgs())
End Sub
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
If Not bw.IsBusy = True Then
' Enable the 'More >>' button on the form, as there will now be details for users to view
Me.btnMore.Enabled = True
' Update the form control settings so that they correctly formatted when the processing starts
set_form_on_start()
bw.RunWorkerAsync()
End If
End Sub
' Other functions exist here
End Class
You should send a button as sender into the event handler:
btnStart_Click(btnStart, New EventArgs())
Steps in involved in raising an event is as follows,
Public Event ForceManualStep As EventHandler
RaiseEvent ForceManualStep(Me, EventArgs.Empty)
AddHandler ForceManualStep, AddressOf ManualStepCompletion
Private Sub ManualStepCompletion(sender As Object, e As EventArgs)
End Sub
So in your case, it should be as below,
btnStart_Click(btnStart, EventArgs.Empty)
Just Call
btnStart.PerformClick()
You are trying to implement a bad idea. Actually, you have to make a subroutine to accomplish these kind of tasks.
Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
call SeparateSubroutine()
End Sub
private sub SeparateSubroutine()
'Your code here.
End Sub
And then whereever you want to call the btnStart's click event, just call that SeparateSubroutine. This should be a correct way in your case.
You can subclass the button and make its OnClick Method public as I described here.