VB.NET Scope with Shared: Subs and Functions and Web Methods - vb.net

.NET Gurus,
I have picked up another developers work and I have created a Class Level Public Property boolean isNewValue
However, I cannot access this Property from the "Shared" Subs or Functions that are Public or <WebMethods> that are Public Shared Functions.
I do not understand why, could someone explain?
' Public Property scoped as Public in the Public Class Class_Name
Public Property isNewValueCode As Boolean
Get
Return _isNewValueCode
End Get
Set(ByVal value As Boolean)
_isNeValuewSrvCode = value
End Set
End Property
Later on in the code I have:
Shared Function GetDataItem(ByRef db As myEntities) As CodeDataItem
THIS IS WHERE I NEED TO BE ADD AN IF
If isNewValueCode Then 'Cannot see the isNewValueCode
Dim data As New CodeDataItem
Dim code = db.tbl_services.FirstOrDefault(Function(x) x.id = TargetID)
If (Not code Is Nothing) Then
data = New CodeDataItem(code)
End If
Else
'New code going against different db.tables in Entity Context
End If
Return data
End Function
enter code here

You can not access non shared class members from shared functions without an instance of that class. So to be able to access isNewValueCode you need an instance of what ever class the property belongs to.
Depending on you requirements you could possibly change the property to be shared also, but than it would no longer be a member of any instance of that class.

I imagine you have something simular to this:
Private _isNewValueCode As Boolean
Private _isNeValuewSrvCode As Boolean
Public Property isNewValueCode As Boolean
Get
Return _isNewValueCode
End Get
Set(ByVal value As Boolean)
_isNeValuewSrvCode = value
End Set
End Property
You have a couple of options here, you can make isNewValue and it's private constructors shared like so:
Private Shared _isNewValueCode As Boolean
Private Shared _isNeValuewSrvCode As Boolean
Public Shared Property isNewValueCode As Boolean
Get
Return _isNewValueCode
End Get
Set(ByVal value As Boolean)
_isNeValuewSrvCode = value
End Set
End Property
Or Assuming isNeValuewSrvCode is a copy paste error and you really wanted to assign it back to the same variable, you could simply do this:
Public Shared Property isNewValueCode As Boolean
Take a look at the MSDN Description of shared properties for more info on what exactly the shared keyword does.

Related

Binding Checkbox to a Shared Property in a custom class

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.

Get a Field Object, not FieldInfo, from a VB Class Instance

I am trying to iterate through objects (fields) in a class and invoke a method on each object. Each object is of a different type. Here is the parent class:
Public Class MySettings
Public IdentifyByFacType As RadioButtonSetting
Public WtrFacTypes As ListSetting
Public OilFacTypes As ListSetting
Public GroupByRef As CheckboxSetting
Public GroupRefAttr As TxtboxSetting
End Class
Here is part of one of the sub-object classes:
<Serializable>
Public Class TxtboxSetting
<XmlIgnore()>
Public MyControl As Windows.Forms.TextBox
<XmlIgnore()>
Public DefaultSetting As String
Private _SavedSetting As String
Public Property SavedSetting As String
Get
Return _SavedSetting
End Get
Set(value As String)
_SavedSetting = value
CurrentValue = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(DefaultSetting As String, MyControl As Windows.Forms.TextBox)
Me.DefaultSetting = DefaultSetting
Me.MyControl = MyControl
End Sub
Public Sub RestoreDefault()
CurrentValue = DefaultSetting
End Sub
End Class
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
So I will have several classes like the MySettings class, and each one will have different sub-objects. Given an instance of such a class, I want to automatically iterate through the fields and call a method RestoreDefault on each one. I don't want to have to know what objects exist in the MySettings class. Rather, knowing that they all have the RestoreDefaultmethod, I want simply call the method on each object.
Despite much searching, I have not found a way to do this. With reflection, I can only get this far:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
RestoreDefault.Invoke(Opts, Nothing)
Next
However, in the line RestoreDefault.Invoke(Opts, Nothing), I can't just pass in Opts, as I am dealing with a field in Opts, not Opts itself. A statement like this would work: RestoreDefault.Invoke(Opts.GroupRefAttr, Nothing), but that requires me to know the objects in the MySettings class ahead of time, and that defeats the purpose. Is there a way to grab field instance objects at runtime and pull this off?
When you invoke the RestoreDefault method you need to invoke it on the setting (i.e., the value of the field), not the class containing the setting. Changing your code to this should fix your problem:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim setting As Object = var.GetValue(Opts)
Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
RestoreDefault.Invoke(setting, Nothing)
Next
However, if you introduce either a base class or an interface you should be able to get rid of some or all of the reflection. The container setting class can have a collection of settings that each have a shared base class or interface with a RestoreDefault method. The container setting class will then call this method through the base class or interface without having to use reflection.
The base class:
Public MustInherit Class BaseSetting
Public MustOverride Sub RestoreDefault
End Class
A specific settings class:
Public Class TxtboxSetting
Inherits BaseSetting
Public Overrides Sub RestoreDefault()
' Specific implementation
End Sub
End Class
On any class deriving from BaseSetting you can now call the RestoreDefault method without having to use reflection.
However, considering your design you might still want to use reflection to get the settings containd in the MySettings class. You can do it like this:
Dim settings = From fieldInfo in Opts.GetType.GetFields
Where GetType(BaseSetting).IsAssignableFrom(fieldInfo.FieldType)
Select DirectCast(fieldInfo.GetValue(Opts), BaseSetting)
For Each setting In settings
setting.RestoreDefault()
Next
Reflection is used to find all the fields deriving from BaseSetting and then RestoreDefault is called on each field. This method does not suffer from the "magic string" problem where your code depends on the name of the RestoreDefault method represented in a string.
(Also, calling the MySettings class the parent is a bit misleading because there is nothing inheriting from MySettings. Instead this class contains other settings.)
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
In that case, the sub-object types should be defined such that they implement a common interface that demands these same methods and properties exist. For now, I'll name that interface IControlSetting. Then, your For loop looks something like this:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim setting As IControlSetting = TryCast(var.GetValue(Opts), IControlSetting)
If setting Is Nothing Then Continue
setting.RestoreDefault()
Next
Additionally, I'd change your MySettings type to encapsulate a dictionary or IControlSetting objects. Then you can just iterate the dictionary to check each of the objects, rather than needing reflection. That might look like this:
Public Class MySettings
Private allSettings As Dictionary(Of String, IControlSetting)
Public Sub New()
allSettings = new Dictionary(Of String, IControlSetting)()
allSettings.Add("IdentifyByFacType", New RadioButtonSetting())
allSettings.Add("WtrFacTypes", New ListSetting())
allSettings.Add("OilFacTypes", New ListSetting())
'...
End Sub
Public Property IdentifyByFacType As RadioButtonSetting
Get
Return DirectCast(allSettings("IdentifyByFacType"), RadioButtonSetting)
End Get
'The setters may be optional, depending on how you expect to use these
Set(ByVal value As RadioButtonSetting)
allSettings("IdentifyByFacType") = value
End Set
End Property
Public Property WtrFacTypes As ListSetting
Get
Return DirectCast(allSettings("WtrFacTypes"), RadioButtonSetting)
End Get
Set(ByVal value As ListSetting)
allSettings("WtrFacTypes") = value
End Set
End Property
Public Property OilFacTypes As ListSetting
Get
Return DirectCast(allSettings("OilFacTypes"), RadioButtonSetting)
End Get
Set(ByVal value As ListSetting)
allSettings("OilFacTypes") = value
End Set
End Property
'...
Public Sub RestoreAllDefaults()
For Each setting As KeyValuePair(Of String, IControlSetting) In allSettings
setting.Value.RestoreDefault()
Next setting
End Sub
End Class

Custom CollectionEditor never triggers a property's "set" method

I am trying to implement a way of persisting a collection in a custom settings class. I have successfully created the settings class (inheriting ApplicationSettingsBase) and can save properties using the built-in editors on a PropertyGrid, but my custom implementation of a property grid for collections doesn't persist any of the values I add. Here's my code:
Imports System.Configuration
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.ComponentModel.Design
Public Class CustomSettings
Inherits ApplicationSettingsBase
<UserScopedSetting()> _
<DefaultSettingValue("White")> _
Public Property BackgroundColor() As Color
Get
BackgroundColor = Me("BackgroundColor")
End Get
Set(ByVal value As Color)
Me("BackgroundColor") = value
End Set
End Property
<UserScopedSetting()> _
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
<Editor(GetType(CustomStringCollectionEditor), GetType(UITypeEditor))> _
Public Property EmailAddresses() As Collection
Get
EmailAddresses = Me("EmailAddresses")
End Get
Set(ByVal value As Collection)
Me("EmailAddresses") = value
End Set
End Property
End Class
Public Class CustomStringCollectionEditor
Inherits CollectionEditor
Public Sub New()
MyBase.New(GetType(Collection))
End Sub
Protected Overrides Function CreateInstance(ByVal itemType As System.Type) As Object
Return String.Empty
End Function
Protected Overrides Function CreateCollectionItemType() As System.Type
Return GetType(String)
End Function
End Class
I set a breakpoint on the Set methods for both the BackgroundColor property and the EmailAddresses property. The BackgroundColor property works as it should - it breaks on the Set statement and stores the property correctly. But when I close the custom CollectionEditor dialog, the EmailAddresses "Set" method is never called. How can I get my custom editor to actually save the property once it's done being edited?
I think I fixed it (with help from this question). I added an override to the EditValue function in my custom editor. Here is the code:
Public Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
Dim result As Object = MyBase.EditValue(context, provider, value)
DirectCast(context.Instance, CustomSettings).EmailAddresses = DirectCast(result, List(Of String))
Return result
End Function
I also moved from a collection to a list - I read somewhere that was a safer way to go. I also added a constructor to my CustomSettings class that set the EmailAddresses property to a new List(Of String) if it was unset to begin with. I found that the first time it ran, I could edit the list and add items, but they wouldn't be persisted:
Public Sub New()
If Me("EmailAddresses") Is Nothing Then
Me("EmailAddresses") = New List(Of String)
End If
End Sub
And now it's all working like it should. But if this isn't the best way or there's an easier way to do it, please chime in.

Create Custom Control with reusable properties

This is similar to my last post but with a different purpose.
I have built a custom control, but when I set the properties for it... ALL instances of that control on my page grab the exact same property. I need to be able to set the property to "abc" for one instance of the control on my page, and then set the exact same proprty to "xyz" for a different instance of the control on the same page.
Can anyone shed any light?
Namespace CustomControl
Public Class mycontrol : Inherits Literal
Protected Overrides Sub CreateChildControls()
Dim value As String = "test"
If _doubleit = True Then
value = value & " test"
End If
MyBase.Text = value
MyBase.CreateChildControls()
End Sub
Private Shared _doubleit As Boolean = True
Public Shared Property doubleit() As Boolean
Get
Return _doubleit
End Get
Set(ByVal value As Boolean)
_doubleit = value
End Set
End Property
End Class
End Namespace
Remove the Shared from your variable and from your property declaration. Shared means exactly that what you don't want: All instances share the same value.
So, your code should look like this:
Private _doubleit As Boolean = True
Public Property doubleit() As Boolean
Get
Return _doubleit
End Get
Set(ByVal value As Boolean)
_doubleit = value
End Set
End Property

How do I modify a ReadOnly Linq property

I have a LINQ to SQL generated class with a readonly property:
<Column(Name:="totalLogins", Storage:="_TotalLogins", DbType:="Int", UpdateCheck:=UpdateCheck.Never)> _
Public ReadOnly Property TotalLogins() As System.Nullable(Of Integer)
Get
Return Me._TotalLogins
End Get
End Property
This prevents it from being changed externally, but I would like to update the property from within my class like below:
Partial Public Class User
...
Public Shared Sub Login(Username, Password)
ValidateCredentials(UserName, Password)
Dim dc As New MyDataContext()
Dim user As User = (from u in dc.Users select u where u.UserName = Username)).FirstOrDefault()
user._TotalLogins += 1
dc.SubmitChanges()
End Sub
...
End Class
But the call to user._TotalLogins += 1 is not being written to the database? Any thoughts on how to get LINQ to see my changes?
Set the existing TotalLogins property as either private or protected and remove the readonly attribute. You may also want to rename it e.g. InternalTotalLogins.
Then create a new property by hand in the partial class that exposes it publically as a read-only property:
Public ReadOnly Property TotalLogins() As System.Nullable(Of Integer)
Get
Return Me.InternalTotalLogins
End Get
End Property
Make a second property that is protected or internal(?)
<Column(Name:="totalLogins", Storage:="_TotalLogins", DbType:="Int", UpdateCheck:=UpdateCheck.Never)> _
protected Property TotalLogins2() As System.Nullable(Of Integer)
Get
Return Me._TotalLogins
End Get
Set(byval value as System.Nullable(Of Integer))
Return Me._TotalLogins
End Get
End Property
and then update that . I think it won't save readonly properties by default. And why should it anyway. I now it's a hack but hey that's life.
Before you change the field call SendPropertyChanging() and then after call SendPropertyChanged("").
This will make the Table entity tracking know something changed.