How do you create a cancelable event in vb.net - vb.net

IN VB.NET (not c#)...
I want to create an event than can be canceled by the listener. Just like you can cancel the closing event of a winforms form in which case the form won't close.
I have already implemented a derived class from EventArgs that has a settable Cancel property as follows:
Public Class AnnounceNavigateEventArgs
Inherits EventArgs
Private _cancel As Boolean = False
''' <summary>
''' Initializes a new instance of the AnnounceNavigateEventArgs class.
''' </summary>
Public Sub New(ByRef cancel As Boolean)
_cancel = cancel
End Sub
Public Property Cancel() As Boolean
Get
Return _cancel
End Get
Set(ByVal value As Boolean)
_cancel = value
End Set
End Property
End Class
Notice that I am passing the cancel argument byRef to the constructor.
The listener I have setup is setting the property to Cancel=True. I thought ByRef meant that both _cancel and cancel would point to the same location on the stack and that setting _cancel=true would therefore make the cancel = true. But this is not the behavior I am getting. _cancel becomes true in the setter but I guess the argument to the constructor remains false.
What is the proper way to do this in vb.net?
Seth

You can re-use the System.ComponentModel.CancelEventArgs class in the .NET framework.
Public Event Announcing As EventHandler(Of AnnounceNavigateEventArgs)
Protected Sub OnAnnounce()
Dim e As New AnnounceNavigateEventArgs
RaiseEvent Announcing(Me, e)
If Not e.Cancel Then
' announce
End If
End Sub
Public Class AnnounceNavigateEventArgs
Inherits System.ComponentModel.CancelEventArgs
End Class

Related

Hide form's DoubleBuffered property without make it nonfunctional

Using a Class I am trying to hide the DoubleBuffered property from form's property window but without make it nonfunctional. So I did something like this in code example below... Ηowever, DoubleBuffered property still appears. So, can we really hide DoubleBuffered property and if yes, how can we do that?
Imports System.ComponentModel
Imports System.ComponentModel.Design
Public Class MyForm
Inherits Form
<Browsable(False)>
Public Overloads Property DoubleBuffered As Boolean
Get
Return MyBase.DoubleBuffered
End Get
Set(ByVal value As Boolean)
MyBase.DoubleBuffered = value
End Set
End Property
Public Sub New()
Me.DoubleBuffered = True
End Sub
End Class
You could create a custom component designer for your Form, but that is a daunting task to just recreate the functionality of the inaccessible System.Windows.Forms.Design.FormDocumentDesigner. The simpler way is use the Form's Site property as I have shown you before to access the designer services.
In this case, you need to override the ITypeDescriptorFilterService service of the designer host. This service is used by the designer for all type discovery/filtering operations and is not limited to a specific component.
The first step is to create a class that implements ITypeDescriptorFilterService. The following is one such implementation. It is a generic implementation that allows it to filter components of the specified type and takes list of property names that you want to exclude from the PropertyGrid display. The final item it requires is a reference to the existing service used by the designer host.
Friend Class FilterService(Of T) : Implements ITypeDescriptorFilterService
Private namesOfPropertiesToRemove As String()
Public Sub New(baseService As ITypeDescriptorFilterService, ParamArray NamesOfPropertiesToRemove As String())
Me.BaseService = baseService
Me.namesOfPropertiesToRemove = NamesOfPropertiesToRemove
End Sub
Public ReadOnly Property BaseService As ITypeDescriptorFilterService
Public Function FilterAttributes(component As IComponent, attributes As IDictionary) As Boolean Implements ITypeDescriptorFilterService.FilterAttributes
Return BaseService.FilterAttributes(component, attributes)
End Function
Public Function FilterEvents(component As IComponent, events As IDictionary) As Boolean Implements ITypeDescriptorFilterService.FilterEvents
Return BaseService.FilterEvents(component, events)
End Function
Public Function FilterProperties(component As IComponent, properties As IDictionary) As Boolean Implements ITypeDescriptorFilterService.FilterProperties
' ref: ITypeDescriptorFilterService Interface: https://msdn.microsoft.com/en-us/library/system.componentmodel.design.itypedescriptorfilterservice(v=vs.110).aspx
'
' The return value of FilterProperties determines if this set of properties is fixed.
' If this method returns true, the TypeDescriptor for this component can cache the
' results. This cache is maintained until either the component is garbage collected or the Refresh method of the type descriptor is called.
' allow other filters 1st chance to modify the properties collection
Dim ret As Boolean = BaseService.FilterProperties(component, properties)
' only remove properties if component is of type T
If TypeOf component Is T AndAlso Not (properties.IsFixedSize Or properties.IsReadOnly) Then
For Each propName As String In namesOfPropertiesToRemove
' If the IDictionary object does not contain an element with the specified key,
' the IDictionary remains unchanged. No exception is thrown.
properties.Remove(propName)
Next
End If
Return ret
End Function
End Class
Example Usage in Form:
Imports System.ComponentModel
Imports System.ComponentModel.Design
Public Class TestForm : Inherits Form
Private host As IDesignerHost
Private altTypeDescriptorProvider As FilterService(Of TestForm)
' spelling and character casing of removedPropertyNames is critical
' it is a case-sensative lookup
Private Shared removedPropertyNames As String() = {"DoubleBuffered"}
Public Overrides Property Site As ISite
Get
Return MyBase.Site
End Get
Set(value As ISite)
If host IsNot Nothing Then
UnwireDesignerCode()
End If
MyBase.Site = value
If value IsNot Nothing Then
host = CType(Site.GetService(GetType(IDesignerHost)), IDesignerHost)
If host IsNot Nothing Then
If host.Loading Then
AddHandler host.LoadComplete, AddressOf HostLoaded
Else
WireUpDesignerCode()
End If
End If
End If
End Set
End Property
Private Sub HostLoaded(sender As Object, e As EventArgs)
RemoveHandler host.LoadComplete, AddressOf HostLoaded
WireUpDesignerCode()
End Sub
Private Sub WireUpDesignerCode()
AddFilter()
End Sub
Private Sub UnwireDesignerCode()
If host IsNot Nothing Then
RemoveFilter()
End If
host = Nothing
End Sub
Private Sub AddFilter()
Dim baseFilter As ITypeDescriptorFilterService = CType(host.GetService(GetType(ITypeDescriptorFilterService)), ITypeDescriptorFilterService)
If baseFilter IsNot Nothing Then
' remove existing filter service
host.RemoveService(GetType(ITypeDescriptorFilterService))
' create our replacement service and add it to the host's services
altTypeDescriptorProvider = New FilterService(Of TestForm)(baseFilter, removedPropertyNames)
host.AddService(GetType(ITypeDescriptorFilterService), altTypeDescriptorProvider)
TypeDescriptor.Refresh(Me.GetType) ' force a type description rescan
End If
End Sub
Private Sub RemoveFilter()
If altTypeDescriptorProvider IsNot Nothing Then
host.RemoveService(GetType(ITypeDescriptorFilterService))
host.AddService(GetType(ITypeDescriptorFilterService), altTypeDescriptorProvider.BaseService)
altTypeDescriptorProvider = Nothing
End If
End Sub
End Class
Now when you create a form that inherits from TestForm, the DoubleBuffered property will be excluded from the PropertyGrid display.

in vb.net how do I raise an event of another control

Every sample I've seen that refers to this is about mouse clicks and then the example is the same.
I need to specifically raise an event on another control.
I have a panel with an event that I created like this:
Private FlowPanel as new my_FlowLayoutPanel
Addhandler FlowPanel.change, addressof doChange
Public Class my_FlowLayoutPanel
Inherits FlowLayoutPanel
Public Event change(ByVal sender As Object)
Public Const Ver_SCROLL As Integer = &H115
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = Ver_SCROLL Then
RaiseEvent change(Me)
End If
MyBase.WndProc(m)
End Sub
End Class
So when the vertical scroll bar moves, the "change" event fires.
So now, I have another control, (a simple panel) set up like this:
Public Class view_Panel
Inherits System.Windows.Forms.Panel
Protected Overrides Sub WndProc(ByRef m As Message)
Const NCMOUSEMOVE As Integer = &H200
If m.Msg = NCMOUSEMOVE Then
' *** FIRE THE "CHANGE" EVENT ON THE FLOWLAYOUT PANEL
End If
MyBase.WndProc(m)
End Sub
End Class
So, how do I fire the "Change" event from the view_Panel?
Even after reading other answers such as 'Pouya Samie' above (reflect to OnChange if available), or this more enhanced article "Raising Events Using Reflection" which seems much cleaner but doesn't always work (reflect to MulticastDelegate)...
So finally got to put all my ideas in order for a generic method to perform this task with a simple syntax:
TriggerEvent(ComboBox1, "OnSelectedIndexChanged")
Notice that the above method is private non-accesible inside the ComboBox1, it is even NOT listed on the IntelliSense list members, but with this reflection method it will work OK:
''' <summary>
''' Manually trigger an existing event in a control.
''' </summary>
''' <param name="ctrlObject">The GUI control that that should be operated (such as ComboBox).</param>
''' <param name="onEventName">The OnEvent function regardless of the scope (such as OnSelectedIndexChanged).</param>
''' <returns><code>True</code> when the method is found invoked and returned successfully; <code>false</code> otherwise.</returns>
Public Function TriggerEvent(ctrlObject As Control, onEventName As String) As Boolean
' Get the reference to the method
Dim methodRef As MethodInfo = ctrlObject.GetType().GetMethod(onEventName, _
System.Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static Or _
System.Reflection.BindingFlags.Instance)
If IsNothing(methodRef) Then Return False
' Invoke the method
Try
methodRef.Invoke(ctrlObject, New Object() {EventArgs.Empty})
Return True
Catch ex As Exception
Return False
End Try
End Function
EDIT
To Call your Event From Another Class you can use reflection
MethodInfo onchange = YourClassInstance.GetType().GetMethod("OnChange", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
onChange.Invoke(YourClassInstance, new object[] { new EventArgs() });
You probably want something like this:
Case WM_HSCROLL
RaiseEvent Scroll(Me, New ScrollEventArgs(ScrollEventType.EndScroll, _
Win32.GetScrollPos(Me.Handle, Win32.SBOrientation.SB_HORZ), _
ScrollOrientation.HorizontalScroll))
Case WM_VSCROLL
RaiseEvent Scroll(Me, New ScrollEventArgs(ScrollEventType.EndScroll, _
Win32.GetScrollPos(Me.Handle, Win32.SBOrientation.SB_VERT), _
ScrollOrientation.VerticalScroll))
ScrollEventArgs is a standard Net event, so we dont need to define it. Then declare the event as ('change' seems a very poor choice):
Public Event Scroll(ByVal sender As Object, ByVal sa As ScrollEventArgs)
If your Panel needs to do something with the event, use the OnScroll method, which allows the panel to do stuff before the end subscriber gets the event:
Protected Overrides Sub OnScroll(ByVal sa As ScrollEventArgs)
... do stuff
' in cases where you no longer need the event to be passed
' on, dont call this:
MyBase.OnScroll(e)
End Sub
How to use:
Since you subclass both, let the Panel raise the event, the FlowPanel can monitor those events (subscribe to the panel's scroll event) and in that, do whatever you were going to do in Change. Since the ACTION takes place in/on the panel, better to just handle it there.

Unit Testing RaiseEvent in Vb.net using MSTest+MSFakes only

Conceptually, I am a little bit lost on Events themselves, so any information here would be helpful.
Specifically, I have a very simple Property Setter which raises an event, and I want to confirm
that this event is raised and also
that the proper parameters have been passes to said event. (although 2. may be unnecessary for this particular event.)
I am trying to contain the overhead in my Unit Testing project, that is, avoid any additional coding in the project being tested.
The code is as follows.
Public Class myItem
Shared Event ItemOpened As EventHandler(Of EventArgs)
.........
Public Property Open() As Boolean
Get
Return mOpen
End Get
Set(ByVal value As Boolean)
mOpen = value
RaiseEvent ItemOpened(Me, New EventArgs)
End Set
End Property
All code is being done in VB.net, which is primary reason why I have not found a good enough resource online for this yet. And also, I am not using a third-party mocking framework such as Nunit or RhinoMock, only MS built in Unit Testing frameworks in VS2012.
Also, similarly, I would like to test FirePropertyChangedNotification() on certain setter methods such as follows....
Public Property myBool() As Boolean
Set(ByVal Value As Boolean)
FirePropertyChangedNotification("myBool")
mViewOnly = Value
End Set
End Property
In which FirstPropertyChangedNotification() is as follows.....
Protected Sub FirePropertyChangedNotification(ByVal propName As String)
If Not Initializing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
RaiseEvent EntityChanged(Me, New PropertyChangedEventArgs(propName))
End If
End Sub
I'm not sure why you want to use Fakes here... You can just use a delegate in you test to subscribe to your event. Now change the property from the test and set a boolean or even place the assert in the delegate.
This is what I'd do in C#, autoconverted to VB.NET (which in my case is very rusty...) This works on my machine. Any improvements from a VB.NET expert are welcome:
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.ComponentModel
Namespace UnitTestProject2
Public Class Sut
Implements INotifyPropertyChanged
Public Property StringProperty() As String
Get
Return String.Empty
End Get
Set(value As String)
OnPropertyChanged("StringProperty")
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
End Class
<TestClass> _
Public Class UnitTest1
<TestMethod> _
Public Sub TestMethod1()
Dim sut As New Sut()
Dim triggered = False
AddHandler sut.PropertyChanged, Sub(o, e)
Assert.AreEqual("StringProperty", e.PropertyName)
triggered = True
End Sub
sut.StringProperty = "test"
Assert.IsTrue(triggered)
End Sub
<TestMethod> _
Public Sub TestMethod2()
Dim sut As New Sut()
Dim triggered = False
AddHandler sut.PropertyChanged, Sub(o, e)
Assert.AreSame(sut, o)
triggered = True
End Sub
sut.StringProperty = "test"
Assert.IsTrue(triggered)
End Sub
End Class
End Namespace

Binding control property to user control property

I have a user control that has some public properties (like Dirty :boolean) and an event (ControlValueChanged) that change that property.
I added that control to a form. In the form I have a button (btnOK) and I want to bind the property Enabled of the button to the Dirty property.
I read http://msdn.microsoft.com/en-us/library/ms229614.aspx but I face some problems to implement this to my project.
My code in the form:
btnOK.DataBindings.Add("Enabled", Me.wwdp, "Dirty") 'wwdp is my user Control
So from my research I have to add in my custom control:
Imports System.ComponentModel
Public Class wwDynamicPanel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Public Property Dirty As Boolean
Get
Return mbDirty
End Get
Set(ByVal value As Boolean)
mbDirty = value
NotifyPropertyChanged()
End Set
End Property
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
I get an error : Type 'CallerMemberName' is not defined.
The thing is that I haven't found in msdn anything more.
I am very sorry. The link in MSDN was for framework 4.5
I found the right http://msdn.microsoft.com/en-us/library/ms184414(v=vs.100).aspx. for my framework
and I solved the problem.
I am leaving the question because someone else find it useful.
So the working code is:
Imports System.ComponentModel
Public Class wwDynamicPanel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Public Property Dirty As Boolean
Get
Return mbDirty
End Get
Set(ByVal value As Boolean)
mbDirty = value
NotifyPropertyChanged("Dirty")
End Set
End Property
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub

How do you handle PropertyChange for a boolean value?

I'm creating a program in which I have a publicly defined boolean value
Public boolOverallStatus As Boolean = True
and I need to execute some code whenever the boolean value changes. In previous applications, an actual form item change handled this, but it can be changed by several different subs.
How would I handle this? I'm looking through msdn, but it's rather confusing.
In a nutshell: How to execute code when the event of a boolean value changing occurs.
Make it a property instead.
Private _boolOverallStatus As Boolean = True
Property boolOverallStatus As Boolean
Get
Return _boolOverallStatus
End Get
Set(ByVal value as Boolean)
If value <> _boolOverallStatus Then
_boolOverallStatus = value
'// handle more code changes here.'
End If
End Set
End Property
I use the following pattern which resembles what Microsoft do for most of their Changed events.
Class MyClass
Public Property OverallStatus As Boolean
Get
Return _OverallStatus
End Get
Set (value As Boolean)
If _OverallStatus = value Then Exit Property
_OverallStatus = value
OnOverallStatusChanged(EventArgs.Empty)
End Set
End Property
Private _OverallStatus As Boolean = False
Protected Overridable Sub OnOverallStatusChanged(e As EventArgs)
RaiseEvent OverallStatusChanged(Me, e)
End Sub
Public Event OverallStatusChanged As EventHandler
End Class
In VB, you can handle the event using the WithEvents and Handles keywords:
Class MyParent
Private WithEvents myObject As New MyClass()
Private Sub myobject_OverallStatusChanged(sender As Object, e As EventArgs) Handles myObject.OverallStatusChanged
' TODO: Write handler.
End Sub
End Class
The OnOverallStatusChanged function is useful for inheriting classes to get first shot at responding to the change.
Class MyOtherClass
Inherits MyClass
Protected Overrides Sub OnOverallStatusChanged(e As EventArgs)
' TODO: Do my stuff first.
MyBase.OnOverallStatusChanged(e)
End Sub
End Class
Use public properties instead of public variables. You can then put logic in the Set method of the property to execute whenever the property is.. well set.
http://msdn.microsoft.com/en-us/library/65zdfbdt%28v=VS.100%29.aspx
Private number As Integer = 0
Public Property MyNumber As Integer
' Retrieves number.
Get
Return number
End Get
' Assigns to number.
Set
CallLogicHere()
number = value
End Set
End Property
You could also define an event, which is fired each time the status changes. The advantage is, that changes can be handled by the parts of the application that depend on this status. Otherwise the logic would have to be implemented together with the status.
Other parts of the application can subscribe the event with AddHandler.
Public Class OverallStatusChangedEventArgs
Inherits EventArgs
Public Sub New(newStatus As Boolean)
Me.NewStatus = newStatus
End Sub
Private m_NewStatus As Boolean
Public Property NewStatus() As Boolean
Get
Return m_NewStatus
End Get
Private Set
m_NewStatus = Value
End Set
End Property
End Class
Module modGlobals
Public Event OverallStatusChanged As EventHandler(Of OverallStatusChangedEventArgs)
Private m_boolOverallStatus As Boolean = True
Public Property BoolOverallStatus() As Boolean
Get
Return m_boolOverallStatus
End Get
Set
If Value <> m_boolOverallStatus Then
m_boolOverallStatus = Value
OnOverallStatusChanged(Value)
End If
End Set
End Property
Private Sub OnOverallStatusChanged(newStatus As Boolean)
RaiseEvent OverallStatusChanged(GetType(modGlobals), New OverallStatusChangedEventArgs(newStatus))
End Sub
End Module