I have made a custom event. What I intend to do is make a custom task pane invisible when I click the close button however it doesn't run my MainTaskPaneControl_HideTaskPane method. I'm clearly missing something simple but I'm unsure what I'm missing.
Code that runs first in ThisAddIn class:
Dim gen = New PowerPointDocSetUpMain()
AddHandler gen.HideTaskPane, AddressOf MainTaskPaneControl_HideTaskPane
Cancel Button in PowerPointDocSetUp class:
Private Sub ButtonCancel_Click(sender As Object, e As EventArgs) Handles ButtonCancel.Click
Dim main As PowerPointDocSetUpMain = New PowerPointDocSetUpMain
main.CloseMain()
End Sub
PowerPointDocSetUpMain class:
Public Event HideTaskPane()
Public Function CloseMain()
RaiseEvent HideTaskPane()
End Function
MainTaskPaneControl_HideTaskPane method in ThisAddIn class:
Friend Sub MainTaskPaneControl_HideTaskPane()
'Hide the requested task pane.
Globals.ThisAddIn.HideTaskPane()
End Sub
HideTaskPane method in ThisAddIn class:
Friend Function HideTaskPane() As System.Windows.Forms.UserControl
myTaskPane.Visible = False
End Function
You have two code snippets there that create PowerPointDocSetUpMain objects, so you're creating two different objects. One of them you register an event handler on and the other one you call CloseMain on. The one you call the method on has no event handler and the one with an event handler doesn't have the method called on it. It's hard to know what the exact solution should be because we don't really know how those code snippets relate to each other but the first two code snippets can't both create new objects. If the first one creates an object and registers an event handler then the second one must call CloseMain on that same object.
You're adding the Handler to the object gen here:
AddHandler gen.HideTaskPane, AddressOf MainTaskPaneControl_HideTaskPane
But in the Button_Click-method a new object main is created without adding a Handler to it. So the Handler for gen is never being called and the one for main does not exist.
Related
I create an object of an inherited class. One of its events is to fire on change in a list. I tried writing up an example, but I found it far easier to put down my logic. By "an event is raised", I mean RaiseEvent. This is also my first time with custom events, and inheritable classes.
Create Object of Inherited Class (in its own thread)
Inherited Class runs MyBase.New()
Base Class starts listening for incoming requests
If request is received and valid, an event from Base Class is raised which is handled by the Inherited Class (with this event a list might be modified)
If list is changed, an event from Inherited Class is raised and is handled by the GUI
Base Class returns to waiting for a new request
I'm trying to avoid editing the GUI from the inherited class. The problem is the event never fires (it ignores the RaiseEvent in Step 5, and the GUI never gets an event). Possibly the separate thread is an issue?
Example of Step 5:
Private Sub RequestReceived(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.RequestReceived
'RequestReceived is from the Base Class
Dim requestType As String = sender
If requestType = "Type1" Then
RequestIsVariantOne(sender)
Else Then
'Handle other variants similarly
End If
End Sub
Private Sub RequestIsVariantOne(ByVal sender As Object)
'Conditional statements go here that determine whether or not to edit the list
'The statements will exit the Sub if it the list should not be edited.
'If Sub hasn't exited yet, now we edit the list.
ThatList.Add(sender)
'ListChanged is from the Inherited class
RaiseEvent ListChanged(ThatList, Nothing)
End Sub
Private Sub ToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles ToolStripMenuItem.Click
Dim rT As New Threading.Thread(AddressOf ListenerThread)
rT.Start()
End If
End Sub
Private Sub ListenerThread()
r = New RequestListener()
*r.Listen()*
End Sub
In asterisks, this was the key element. Originally, it was called from the inherited class' constructor. This meant there was never a return to the GUI, and my class level variable was never updated. Therefore, any references to RequestListener (specifically the Events) wouldn't trigger because RequestListener is Nothing. Calling it from the GUI solved the r IS Nothing issue.
I discovered this by creating a button to raise the event manually, but was greeted with NullReferenceException. Once I started my listener from my GUI, the method handling my custom event was fired. I assume this solves my problem, but will need to work a bit more.
I know there is a lot of information regarding the above, but I do not grasp how to do this correctly, so I thought using a real life problem may help to click it for me and others.
So in class A I have defined an event method
Public Sub textChangedMethod(ByVal textedChanged As Boolean)
' do some code on properties of this class only
End Sub
What I need to happen is I need some other class to raise this method,
I have a concept but its totally wrong.
At the moment I pass an instance of class A to the another class so it can reference the event (this must be wrong)
Dim UI As New newClassDialog(Me) 'class A
In this new class I have the event handler
Public Event textChanged(ByVal textedChanged As Boolean)
So in the constructor of the new class I can now add the handler
Public Sub New(ByRef classA As Class A)
' This call is required by the designer.
InitializeComponent()
AddHandler textChanged, AddressOf classA.textChangedMethod
End Sub
Now of course I can raise the event like so
RaiseEvent textChanged(True)
Basically passing in the class seems ridiculous in my eyes, so using this example is there a 'proper' way of doing this?
Thanks
It seems that you are inverting the roles. In this context the class that raises the event shouldn't know who handles the event. It is the responsability of class that instantiate the newClassDialog to add the event handler for the events raised by the called class
Dim UI As New newClassDialog(Me)
AddHandler UI.textchanged, AddressOf Me.textChangedMethod
To start with I have a fairly unique situation in that I am dealing with large amounts of data - multiple series of about 500,000 points each. The typical plot time is about 1s which is perfectly adequate.
The chart is created 'WithEvents' in code and the plot time doesn't change.
However, when I add the sub with the handler for the click event ..
Private Sub Chart_Main_Click(ByVal sender As Object, _
ByVal e As MouseEventArgs) Handles Chart_Main.Click
Dim y As Integer = Chart_Main.ChartAreas(0).AxisX.PixelPositionToValue(e.X)
'MsgBox(y)
End Sub
the plot time blows out to 3min. Even having no code in the sub, the result is the same. There is no reference to the click event in any of the code so I am at a loss as to why this is occurring. I suspect it has something to do with the number of points being added but not knowing the cause is frustrating.
Is anyone able to explain what is going on?
Ok, i don't know if the explanation in the comments was sufficient, so here some example code...
Also i wanted to try this myself!
Essencially, what you do is take control on when you want Windows to check the events.
For that, i suggested two wrappers on AddHandler and RemoveHandler that can safely be called from worker threads.
So, what you have to do, is:
Initialize the Handler in the constructor
Call RemoveClickHandler on your control, each time you want it to be left alone by the EventHandler
But don't forget to reinitialize the handler afterwards via AddClickHandler
Also, your handler method should not have the 'Handles' keyword anymore...
Public Class MainForm
Public Sub New()
' This call is required by the designer.
InitializeComponent()
m_pPictureClickHandler = New MouseEventHandler(AddressOf hndPictureClick)
AddClickHandler(pbxFirst, m_pPictureClickHandler)
End Sub
' Have a persistent local instance of the delegate (for convinience)
Private m_pPictureClickHandler As MouseEventHandler
Public Sub AddClickHandler(obj As Control, target As [Delegate])
If Me.InvokeRequired Then
Me.Invoke(New Action(Of Control, [Delegate])(AddressOf AddClickHandler), obj, target)
Else
AddHandler obj.MouseClick, target
End If
End Sub
Public Sub RemoveClickHandler(obj As Control, target As [Delegate])
If Me.InvokeRequired Then
Me.Invoke(New Action(Of Control, [Delegate])(AddressOf RemoveClickHandler), obj, target)
Else
RemoveHandler obj.MouseClick, target
End If
End Sub
' Here your Plot is done
Public Sub LockedPlot()
RemoveClickHandler(pbxFirst, m_pPictureClickHandler)
' do something on your handler free control ...
AddClickHandler(pbxFirst, m_pPictureClickHandler)
End Sub
' This is your handler (note without a 'Handles' keyword)
Private Sub hndPictureClick(sender As Object, e As MouseEventArgs)
' do something with the click
MessageBox.Show(String.Format("Yeah! You clicked at: {0}x{1}", e.X.ToString(), e.Y.ToString()))
End Sub
End Class
I suppose an even better design would be to create a child class of your chart that has an LPC style method called, say 'SafePlot', with folowing features:
It accepts a pointer (delegate) to a procedure
It will remove all the event handler before invoking the procedure
Finally it would reinitialize the handlers on it's own after the job is done.
It may require a collection to all handler refering to it's events.
-> For that reason i'd let the class manage the handlers entiraly...
Alternativly you could put the 'SafePlot' idea in your main class. then you could manage the event handler there... but that is disputable
Well i can think of a few other ways to do this, but i'm cutting the brainstorming now!
If interested in one of these design solutions, give me a poke.
I made a class that handles the display of a set of controls. These controls are created at runtime. Because of that, I have to add them to an event handler at runtime as well. I made a function that allows me to specify the event handler to be used for some of the controls. The code looks like this:
Here's the main form
Dim displayObj as PackageDisplay = new PackageDisplay(AddressOf CheckBox_CheckedChanged)
The constructor does this
Public Sub New(ByRef eventHandler as Action(Of System.Object, EventArgs)
AddHandler chkExample.CheckedChanged, eventHandler
End Sub
However, I get the following error:
Value of type 'System.Action(Of Object, System.EventArgs)' cannot be converted to 'System.EventHandler'
It surely must be possible to pass an event handler and assign it, but I just don't know how. I've tried several different variations of this, but I can't figure out how to make this work. Any ideas?
You don't need to make one, use the stock System.EventHandler.
I have a form in VB.NET that is used as a dialog in a mainform. Its instances are always locally defined, there's no field for it. When the user clicks the OK button in the dialog, it will fire an event with exactly one argument, an instance of one of my classes.
Since it is always a local variable, how can I add an event handler for that event? I've searched for myself and found something but I can't really figure it out...
Code for the event, a field in MyDialog:
public Event ObjectCreated(ByRef newMyObject as MyObject)
Code for the main form to call dialog : (never mind the syntax)
Dim dialog As New MyDialog()
dialog.ShowDialog(Me)
AddHandler ObjectCreated, (what do I put here?) //Or how do I add a handler?
As you can see I'm stuck on how to add a handler for my event. Can anyone help me? Preferrably with the best way to do it...
It's recommended, for consistency, that you use the same source and event args model as all system event handlers.
Create your own class inheriting from EventArgs, as:
Public Class MyObjectEventArgs
Inherits EventArgs
Public Property EventObject As MyObject
End Class
Then declare your event, and a handler method, like:
Public Event ObjectCreated As EventHandler(Of MyObjectEventArgs)
Private Sub Container_ObjectCreated(ByVal sender As Object, ByVal e As MyObjectEventArgs)
' Handler code here
End Sub
Then attach the handler to your event using:
AddHandler ObjectCreated, AddressOf Container_ObjectCreated
Additionally, you can use the Handles to attach to the event raised from your main form (assuming the name MainForm), as below:
Private Sub MainForm_ObjectCreated(ByVal sender As Object, ByVal e As MyObjectEventArgs) Handles MainForm.ObjectCreated
' Handler code here
End Sub
You need to write the subroutine that actually executes when the event is generated:
public Sub OnObjectCreated(ByRef newMyObject as MyObject)
...
End Sub
Then the handler is added:
AddHandler ObjectCreated, AddressOf OnObjectCreated
As a side note, ByRef does nothing here. All objects in VB are passed by reference. Only primitave variables (string, int, etc) by default use ByVal and can be set to ByRef