So this should be simple I guess, but I have never done this.
Got a form on which I have a checkbox. I want this check box to be directly linked to a custom class's Boolean property, but somehow this does not work.
Public Class someClass
Private Shared _Filter_Neighbor_6X1 As Boolean = True
Shared Property Filter_Neighbor_6X1() As Boolean
Get
Return _Filter_Neighbor_6X1
End Get
Set(ByVal Value As Boolean)
_Filter_Neighbor_6X1 = Value
End Set
End Property
End Class
Public Class GameGUI
Private Sub GameGUI_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
chk_FilterAll.DataBindings.Add("Checked", someClass, "Filter_Neighbor_6X1")
end sub
End Class
The above does not work. It complains the "someClass" is a class.
I also tried:
chk_FilterAll.DataBindings.Add( _
New Binding("Checked", someClass, "Filter_Neighbor_6X1", _
False, DataSourceUpdateMode.OnPropertyChanged, False))
which hangs the app on start.
any help is appreciated.
Thanks!
You can't bind to a Shared property. You need to remove the Shared modifier from the property, create an instance of the class, and use that instance for the data binding. Your class would look something like this:
Public Class someClass
Private _Filter_Neighbor_6X1 As Boolean = True
Public Property Filter_Neighbor_6X1() As Boolean
Get
Return _Filter_Neighbor_6X1
End Get
Set(ByVal Value As Boolean)
_Filter_Neighbor_6X1 = Value
End Set
End Property
End Class
Then you can do something like:
Dim instanceOfSomeClass As New someClass()
chk_FilterAll.DataBindings.Add("Checked", instanceOfSomeClass, "Filter_Neighbor_6X1")
Note that if you don't have extra logic under the Get or Set of the property, you can get rid of the backing field and convert it to an Auto-Implemented (shorthand) property:*
Public Property Filter_Neighbor_6X1 As Boolean = true
Moreover, if you don't want to create an instance of the class each time, you can make it a Singleton:
Public Class someClass
Private Shared _instance As someClass
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance As someClass
Get
If _instance Is Nothing Then _instance = New someClass()
Return _instance
End Get
End Property
Public Property Filter_Neighbor_6X1 As Boolean = True
End Class
Then, you can use it directly like this:
chk_FilterAll.DataBindings.Add("Checked", someClass.Instance, "Filter_Neighbor_6X1")
* You probably do need extra logic in this particular case in order to notify the change of the property so that the control can reflect that change. Check the other answer for more info.
If you don't want to get rid of the Shared modifier, you can add a read-only property which returns the value of the Shared one:
Public Class someClass
' ...
' ...
' TODO: Remember to use a different name.
Public ReadOnly Property MyNonSharedProperty As Boolean
Get
Return Filter_Neighbor_6X1
End Get
End Property
End Class
Then you can use it like this:
Dim instanceOfSomeClass As New someClass()
chk_FilterAll.DataBindings.Add("Checked", instanceOfSomeClass, "MyNonSharedProperty")
Or with the Singleton approach (check the other answer for details):
chk_FilterAll.DataBindings.Add("Checked", someClass.Instance, "MyNonSharedProperty")
However, please note that if you're implementing the INotifyPropertyChanged interface in your class*, you'll need to notify the change of the read-only property as well as the Shared one when the latter gets changed.
Full implementation:
Public Class someClass
Implements INotifyPropertyChanged
' ##################################
' Optional: Add Singleton logic here
' ##################################
Private _Filter_Neighbor_6X1 As Boolean = True
Public Property Filter_Neighbor_6X1() As Boolean
Get
Return _Filter_Neighbor_6X1
End Get
Set(ByVal Value As Boolean)
_Filter_Neighbor_6X1 = Value
NotifyPropertyChanged()
' TODO: Remember to change the name of the property.
NotifyPropertyChanged("MyNonSharedProperty")
End Set
End Property
' TODO: Remember to use a different name.
Public ReadOnly Property MyNonSharedProperty As Boolean
Get
Return Filter_Neighbor_6X1
End Get
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
* Which is something you should do when using the class as a DataBinding source.
Related
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.
I have a singleton class, but I want its object to be able to raise events.
My current singleton code is as follows:
Private Shared ReadOnly _instance As New Lazy(Of WorkerAgent)(Function() New _
WorkerAgent(), LazyThreadSafetyMode.ExecutionAndPublication)
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance() As WorkerAgent
Get
Return _instance.Value
End Get
End Property
Whenever I change ReadOnly _instance As New.. into ReadOnly WithEvents _instance As New...
I get an error saying ReadOnly is not valid on a WithEvents deceleration
Although I can create the instance in the property itself, but I liked the above code because it is using .NET Lazy keyword which probably have great multithreading benefits.
This isn't an answer to your question as asked but it demonstrates why that question doesn't make sense. It also requires a fair chunk of code so posting in a comment wasn't really an option. This is how your singleton class would raise events, i.e. just like any other class, and how a consumer would handle those events, i.e. just like for any other type.
Singleton:
Public Class WorkerAgent
Private Shared ReadOnly _instance As New Lazy(Of WorkerAgent)
Private _text As String
Public Shared ReadOnly Property Instance As WorkerAgent
Get
Return _instance.Value
End Get
End Property
Public Property Text As String
Get
Return _text
End Get
Set
If _text <> Value Then
_text = Value
OnTextChanged(EventArgs.Empty)
End If
End Set
End Property
Public Event TextChanged As EventHandler
Private Sub New()
End Sub
Protected Overridable Sub OnTextChanged(e As EventArgs)
RaiseEvent TextChanged(Me, e)
End Sub
End Class
Note that the instance event is raised when the instance property changes, just as for any other type, singleton or not.
Consumer:
Public Class Form1
Private WithEvents agent As WorkerAgent = WorkerAgent.Instance
Private Sub agent_TextChanged(sender As Object, e As EventArgs) Handles agent.TextChanged
'...
End Sub
End Class
The field that the single instance is assigned to is where WithEvents gets used. As your error message states, that field cannot be declared ReadOnly too. If they want a ReadOnly field then they need to use AddHandler to handle events.
I can't find an answer to my question so I'm asking a new one.
I have an object where I want to fill it's properties from another class in the same solution. But the object should expose read-only properties only so the outside-caller can't see nor access the setter (cause there is no setter).
What is the best way to fill the internal backing variables from the same solution? I know I could do it in the constructor but I want to be able to set the variables after creating the object.
Sorry for my weird explaination, maybe a bit of code could help.
This is what I'm doing now:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
'Could use this, but don't want to...
Protected Friend Sub New(foo As String)
End Sub
Friend _foo As String
Public ReadOnly Property Foo As String
Get
Return _foo
End Get
End Property
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject._foo = "bar"
'Could use this, but don't want to...want to access properties directly.
Dim roObject2 As New ReadonlyObject("bar")
End Sub
End Class
With this, the ReadonlyObject's properties are correctly exposed as readonly but I'm afraid it's bad practice.
I've seen implementations like this:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
Private _foo As String
Public Property Foo As String
Get
Return _foo
End Get
Friend Set(value As String)
_foo = value
End Set
End Property
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject.Foo = "bar"
End Sub
End Class
This works, but exposes the property with a setter. It's not accessible, but it's visible and I don't want that :)
So maybe it's only a cosmetic thing but I think it's nice to tell the caller (or at least intellisense) the property is strictly read-only.
Thanks, Jan
If you want to explicitly declare the property as read-only, but then still have a way to set it after it is constructed, then all you need to do is create your own setter method rather than using the one automatically created for you but the property. For instance:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
Private _foo As String
Public ReadOnly Property Foo As String
Get
Return _foo
End Get
End Property
Friend Sub SetFoo(value As String)
_foo = value
End Sub
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject.SetFoo("bar")
End Sub
End Class
Or, you could create two properties, like this:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
Public ReadOnly Property Foo As String
Get
Return HiddenFoo
End Get
End Property
Friend Property HiddenFoo As String
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject.HiddenFoo = "bar"
End Sub
End Class
Is it possible to listen to a parent class' object's event via the property accessor?
What I've tried (a minimal example):
Public Class ParentFoo
Private WithEvents m_bar As EventyObj
Public Property Bar() As EventyObj
Get
Return m_bar
End Get
Set(ByVal value As EventyObj)
m_bar = value
End Set
End Property
End Class
Public Class ChildFoo
Inherits ParentFoo
[...]
Public Sub Bar_OnShout() Handles Bar.Shout
' Some logic
End Sub
End Class
The specific error message I'm getting (VS2005) is "Handles clause requires a WithEvents variable defined in the containing type or one of its base types." Does accessing a private WithEvents variable via a public property strip away the 'WithEvents'?
In ParentFoo:
Public Overridable Sub OnShout() Handles m_bar.Shout
'No Logic Necessary
End Sub
In ChildFoo:
Public Overrides OnShout()
'Logic Here
End Sub
Since ParentFoo will call OnShout when m_bar raises a Shout Event and you override it in ChildFoo, your ChildFoo's OnShout will handle that event.
I'm trying to use the type Boolean for some parameter.
<AttributeUsage(AttributeTargets.Method, Inherited:=True, AllowMultiple:=False)>
Public Class MyAttribute
Inherits Attribute
Property MyCondition As Boolean
End Class
I'm facing the problem that, in my object MyAttribute, I'm not able to say if the boolean property has the value False:
because of the presence of the parameter (e: MyCondition:=false)
or,
because no parameter was passed and my property has the value False because of the initialization of my object.
I though about using a property Nullable(Of Boolean) instead, but this doesn't seem to be allowed?
Property MyCondition As Nullable(of Boolean)
Error message "Property or field 'MyCondition' does not have a valid attribute type."
Is there a walk-around this situation? Is the only workable parameter type is String (that is null on instantiation) ?
I believe that you can only use simple data types that can be represented as a literal constant in code (e.g. 100, True, "Test"). That is probably why Nullable(Of Boolean) is not working for you. You can, however, accomplish what you are trying to do by creating actual property handlers:
<AttributeUsage(AttributeTargets.Method, Inherited:=True, AllowMultiple:=False)>
Public Class MyAttribute
Inherits Attribute
Public Property MyCondition As Boolean
Get
Return _myCondition
End Get
Set(ByVal value As Boolean)
_myCondition = value
_explicitlySet = True
End Set
End Property
Private _myCondition As Boolean = False
Public ReadOnly Property ExplicitlySet As Boolean
Get
Return _explicitlySet
End Get
End Property
Private _explicitlySet As Boolean = False
End Class
Alternatively, you could make it a read-only nullable property and use a constructor to set it:
<AttributeUsage(AttributeTargets.Method, Inherited:=True, AllowMultiple:=False)>
Public Class MyAttribute
Inherits Attribute
Public Sub New()
End Sub
Public Sub New(myCondition As Boolean)
_myCondition = myCondition
End Sub
Public ReadOnly Property MyCondition As Nullable(Of Boolean)
Get
Return _myCondition
End Get
End Property
Private _myCondition As Nullable(Of Boolean)
End Class
Use an enumeration instead
public enum TriState
{
NotSet,
False,
True
}
I would use a three class approach
MyAttribute (inherits Attribute)
ValidMyAttribute (interits MyAttribute)
InvalidMyAttribute (inherits MyAttribute)
' means true
<ValidMyAttribute> _
Public Sub MyMethod1()
' means false
<InvalidMyAttribute> _
Public Sub MyMethod2()
' means not defined
<MyAttribute> _
Public Sub MyMethod3()
the classes should look like this
Public class MyAttribute
Inherits Attribute
Public Overridable ReadOnly Property Value as Boolean?
Get
return nothing
End Get
End Property
End Class
Public class ValidMyAttribute
Inherits MyAttribute
Public Overrides ReadOnly Property Value as Boolean?
Get
return true
End Get
End Property
End Class
Public class ValidMyAttribute
Inherits MyAttribute
Public Overrides ReadOnly Property Value as Boolean?
Get
return false
End Get
End Property
End Class
Update:
Just had a second thought and this should be much easier:
<AttributeUsage(AttributeTargets.Method, Inherited:=True, AllowMultiple:=False)>
Public Class MyAttribute
Inherits Attribute
Public Property Value As Boolean? = Nothing
Public Sub New(value As Boolean)
Me.Value = value
End Sub
Public Sub New()
End Sub
End Class
Usage:
<MyAttribute(True)> _
Public Sub Method()
<MyAttribute(false)> _
Public Sub Method()
' Value will be nothing
<MyAttribute()> _
Public Sub Method()