Allowing Users to Handle an event in place of a default event - vb.net

I am building a custom control and what i would like to do is have an event lets call this event OnMenuShow. Now what i would like to be able to do is handle this event inside my control to show one menu but allow the user implementing my custom control to handle the event in inside the parent form to show a different menu if they wish. so the users code would look something like this.
Public Sub Control_OnMenuShow(sender as Object, e as CustomEventArgs) handles Control.OnMenyShow
'DO some work
e.handled = true
end Sub
I'm just not sure on how to prevent the event from firing twice once for the code inside the control the other in the event. if someone could point me in the right direction that would be very helpful
-Nathan

In your control:
Public Event MenuShow As EventHandler
Public Overridable Sub OnMenuShow()
RaiseEvent MenuShow(New EventArgs)
End Sub
Now the consumer may override OnMenuShow which would bypass your raiseevent statement without you needing to "check" anything.

I found that Hans' answer reponse fit my question the best, and had he left it in an answer I would have marked his as answered. I will leave how I ended up coding this for future refrence.
Public Event MenuShow As EventHandler(Of MenuShowEventArgs)
Public Overridable Sub OnMenuShowEvent()
Dim args As New MenuShowEventArgs(False, "Control")
RaiseEvent MenuShow(Me, args)
if args.handled then return
'DO WORK
End Sub

Related

Windows form actions like close through custom user control code?

I have made a title bar (custom user control) that contains five controls. They are all labels but each one do different "job".
For example, one of them is an exit form button. If I put a click event into my custom user control's code, for example...
Private Sub ExitButton_Click(sender As Object, e As EventArgs) Handles ExitButton.Click
Close()
End Sub
I get this error...
BC30451 'Close' is not declared. It may be inaccessible due to its protection level.
On the other hand I can't put it into my project's code cause it can't find ExitButton as "isolated" control and do close().
Any suggestions? I also want to do the same thing with minimize, maximize etc.
Let me guess; your button is in the user control. You try to call Close() on the UserControl class, which obviously is not a window and does not have it.
There are three solutions:
Use the ParentForm property and call Close() on it (e.g. ParentForm.Close()). Easy but not too flexible; if you want to do other things than those which are implemented in the Form base class (like Close()), e.g. specific to the main form, you would have to cast it first and check if it's really the form you thought of. Also, all those things would need to be exposed with Public or Internal, don't expose what you don't have to expose.
You pass the Form to the UserControl. Horrible because passing stuff around just ends up in spaghetti code.
Better, raise an event by the UserControl which you handle in the form the UserControl is on. That's probably the most flexible approach.
Here's a small code example solving this with an event:
Open the code of the UserControl and add an event signature and raise that event when you click the button:
Public Class MyUserControl
Public Event ButtonClicked(sender As Object, e As EventArgs)
Private Sub MyButton_Click(sender As Object, e As EventArgs) Handles MyButton.Click
RaiseEvent ButtonClicked(sender, e)
End Sub
End Class
Then, in your Form, attach to the ButtonClicked event of the UserControl:
Public Class MyForm
Private Sub MyUserControl1_ButtonClicked(sender As Object, e As EventArgs) Handles MyUserControl1.ButtonClicked
Close()
End Sub
End Class
If you re-use the event for multiple buttons, you can check which button it is through the sender passed to the event. (Of course this can be optimized by just passing a casted Button instance as the event parameter, this is just a simple example).
Where did you get "close" from? You exit an application with application.exit()
If you want to close Application you can use:
Application.Exit()
If you want to close Form:
Me.Close()
To close the form you use me.
me.close

User Control Event against Form Control Event

Here is my situation, I have a user control that have the Leave event:
Private Sub MyControl_Leave(sender As Object, e As EventArgs) Handles Me.Leave
If Me.Enabled Then
MsgBox(Property1)
End If
End Sub
I have this to prevent Leave Event from triggering when the control is Disabled.
Then on my form, the control also has its own Leave event because I need to set some Properties that the Leave Event on the User Control needs.
Private Sub myControlOnForm_Leave(sender As Object, e As EventArgs) Handles MyControlOnForm.Leave
MyControlOnForm.Property1 = "value1"
End Sub
What happens is the first event that triggers is the one on the User Control and then the one on the form.
Now my problem is, as the code states above, I need the Form Event to trigger first before the User Control Event.
Is there any work around for this?
The form needs to call a procedure in the user control after it's finished handling the event. Just remove the Handles Me.leave statement, and the private statement. use the sub from your form to call the controls sub which was intended to handle the event.
Note that I've changed sender As object to sender As Mycontrol.
Sub MyControl_Leave(sender As Mycontrol, e As EventArgs)
If Me.Enabled Then
MsgBox(Property1)
End If
End Sub
Code on form
Private Sub myControlOnForm_Leave(sender As Mycontrol, e As EventArgs) Handles MyControlOnForm.Leave
MyControlOnForm.Property1 = "value1"
sender.MyControl_Leave(sender, e)
End Sub
What happens is the first event that triggers is the one on the User Control and then the one on the form.
Now my problem is, as the code states above, I need the Form Event to
trigger first before the User Control Event.
First off, you are using the incorrect term in that problem statement. It is not an event triggering order issue, but rather an issue in order in which the event handlers registered for the UserControl's Leave event execute.
.Net events are a form of syntactic sugar for the invocation of a multicast delegate. When an event is raised a delegate is invoked and the order in which the handlers are executed is the order in which they were added to the delegate. You can gain an understanding of this by working through the various "Walkthrough" tutorials located under Events (Visual Basic).
The Leave event is Raised by calling the Overridable OnLeave method inherited from the Control Class that is in the inheritance tree of the UserControl Class. It is considered bad form for a class to handle its own generated event; the preferred method is Override the method that raise the event.
In your case, you want the form that subscribes to the event to be notified first so that it can modify a property on the UserControl before some it performs some action in response to Leaving the UserControl.
Public Class UserControl1
Protected Overrides Sub OnLeave(e As EventArgs)
MyBase.OnLeave(e) ' this calls the base method that Raises the event
' all event handlers will run before the subsequent code
' executes
If Me.Enabled Then
'do something
End If
End Sub
End Class

Facing an issue while accessing declared event in vb.net

Facing an issue while accessing declared event in vb.net.
Please go thorough below example. (I have modified below stuff to make understable as it is part of one of custom control development)
Public Class Main
Inherits ComboBox
'Event handler for when an item check state changes.
Public Event ItemCheck As ItemCheckEventHandler
Private parentMainClass As Main
Private cclb As controlClass
Public Sub New(parentclass As Main)
Me.parentMainClass = parentclass
'Add a handler to notify our parent of ItemCheck events.
AddHandler Me.cclb.ItemCheck, New System.Windows.Forms.ItemCheckEventHandler(AddressOf Me.cclb_ItemCheck)
End Sub
Private Sub cclb_ItemCheck(sender As Object, e As ItemCheckEventArgs)
'If ccbParent.ItemCheck IsNot Nothing Then
RaiseEvent parentMainClass.ItemCheck(sender,e)
'End If
End Sub
Public Class controlClass
Inherits CheckedListBox
End Class
End Class
Problem: RaiseEvent parentMainClass.ItemCheck(sender,e) this statement shows - ItemCheck event not exists even though it is existed.
Please guide.
Thank You
The event declaration;
Public Event ItemCheck As ItemCheckEventHandler
Should be;
Public Event ItemCheck(sender As Object, e As ItemCheckEventArgs)
What the error is telling you is that it cannot match up the event to the event handler signature.
From your original code, the line;
RaiseEvent parentMainClass.ItemCheck(sender, e)
should be changed to;
RaiseEvent ItemCheck(sender, e)
This raises the ItemCheck event from the current instance. What you seem to be wanting to do is to raise the event on the parent instance, which is a bit different.

VB.Net Chart created in code using WithEvents - Handler causes plot time to increase - why?

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.

Decent way to requery CanExecute in Silverlight MVVM?

I have a simple LoginForm.
Here is how the code-behind looks like:
Private Sub btnLogin_Click(sender As Object, e As RoutedEventArgs) _
Handles btnLogin.Click
If Me.loginForm.ValidateItem() Then
'Do the actual login - (calling VM command)
DirectCast(Me.DataContext, LoginViewModel).LoginCommand.Execute()
End If
End Sub
Now I created a LoginViewModel that exposes a LoginCommand. I would like to keep the code-behind clean, and in the other hand, leave the ViewModel UI independent.
What should be the cleanest way to do this?
I am looking for an application level solution so I can make all the controls UpdateSourceTrigger=PropertyChanged or another workaround whatsoever to requery the CanExecute command when attempting click.
Update after Jon's answer:
So where should I call this method from, should it be the Login?
Private m_LoginCommand As ICommand
Public ReadOnly Property LoginCommand() As ICommand
Get
If m_LoginCommand Is Nothing Then m_LoginCommand =
New DelegateCommand(AddressOf Login, AddressOf CanLogin)
Return m_LoginCommand
End Get
End Property
Private Function CanLogin() As Boolean
Return Not IsLoggingIn
End Function
Private Sub Login()
DirectCast(LoginCommand, DelegateCommand).RaiseCanExecuteChanged()
If Not CanLogin() Then Exit Sub
'Do login
End Sub
It isn't completely clear what your goal is, so I hope I got this right.
Assuming you are using Prism, then whenever LoginCommand's can-execute status is changed (which is done from your ViewModel), the VM should immediately call RaiseCanExecuteChanged on it. This will notify all controls that bind to this command that they need to requery the CanExecute status.
If you are not using Prism, your command class should have some similar mechanism.
In any case, you don't need to do anything from the View.