Create Custom Control with reusable properties - vb.net

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

Related

How to get property of a class using GetValue

I have a couple of classes with properties. I would like to be able to iterate through the properties of the class and get the value of the property. I included a sample of the class along with how I am looping the properties. Need help with understanding the usage of "GetValue" and "SetValue"
Private Sub loadValues()
Dim mySample As New Sample
Dim props As PropertyInfo() = mySample.GetType().GetProperties()
mySample.Test2 = True
Console.WriteLine(mySample.Test2)
For Each prop In props
Console.WriteLine(prop.Name)
'This will loop through all the properties in the Sample clasee
'How can I get and change the value of each of the properties?
prop.GetValue(mySample, Nothing) '?????
prop.SetValue(mySample, False, Nothing) '?????
Next
End Sub
Here is the sample of the class
Public Class Sample
Public Name As String = "Sample"
Public _Cut As Boolean = False
Private _Test1 As Boolean
Private _Test2 As Boolean
Private _Test3 As Boolean
Public Property Cut(ByVal CabType As String) As Boolean
Get
Return _Cut
End Get
Set(ByVal value As Boolean)
_Cut = value
End Set
End Property
Public Property Test1() As Boolean
Get
Return _Test1
End Get
Set(ByVal value As Boolean)
_Test1 = value
End Set
End Property
Public Property Test2() As Boolean
Get
Return _Test2
End Get
Set(ByVal value As Boolean)
_Test2 = value
End Set
End Property
Public Property Test3() As Boolean
Get
Return _Test3
End Get
Set(ByVal value As Boolean)
_Test3 = value
End Set
End Property
End Class
The Cut property is indexed, so you need to pass some index value to PropertyInfo.GetValue. You may see that the signature of GetValue is
Public Overridable Function GetValue(obj As Object, index() As Object) As Object
so the index argument needs to be an array when indexed. Simply pass your Nothing as a single element index {Nothing} in that case. You can tell if there are index parameters by checking if PropertyInfo.GetIndexParameters() has anything
Private Sub loadValues()
Dim mySample As New Sample
Dim props As PropertyInfo() = mySample.GetType().GetProperties()
mySample.Test2 = True
Console.WriteLine(mySample.Test2)
For Each prop In props
Console.WriteLine(prop.Name)
If prop.GetIndexParameters().Any() Then
prop.GetValue(mySample, {Nothing})
prop.SetValue(mySample, False, {Nothing})
Else
prop.GetValue(mySample, Nothing)
prop.SetValue(mySample, False, Nothing)
End If
Next
End Sub
However, having an indexed property then iterating through all the properties arbitrarily may not make sense when you need to pass a specific index to properly access the property.

.NET inherited label doesn't autosize

I'd like to inherit from Windows.Forms.Label, so I've made something like that:
Public Class CustomLabel
Inherits Label
Public Property CustomText As String
Protected Property DefaultText as
Public Overrides Property Text As String
Get
Return If(CustomText <> "", CustomText, MyBase.Text)
End Get
Set(value As String)
MyBase.Text = value
End Set
End Property
End Class
The issue is using this, even if AutoSize property is still true, the label keep it's original size whatever the value set to CustomText.
So I thought that Mybase.Text property isn't updated when CustomText is set to I've changed to :
Public Class CustomLabel
Inherits Label
Public Property CustomText As String
Get
Return _txt
End Get
Set(value As String)
_txt = value
MyBase.Text = value
End Set
End Property
Protected _txt As String
Protected Property DefaultText As String
Public Overrides Property Text As String
Get
Return If(CustomText <> "", CustomText, DefaultText)
End Get
Set(value As String)
DefaultText = value
End Set
End Property
End Class
But still the same issue.
And, on the other side, when I set the Text property on a standard Label in the same place in the code where I set my CustomLabel, the autosize works.
Any idea ? Thanks
After some other tries, I've check how the TextChanged event is raised. It appears that the control is firstly initialized by the designer, and the CustomText is set to nothing, and as expected the TextChanged event is raised.
However, later in the code when CustomText property is set to the wanted value, I don't know why, but the event is not raised, even if the string is strictly different.
So to get a working code I have to write:
Public Property CustomText As String
Get
Return _txt
End Get
Set(value As String)
_txt = value
OnTextChanged(EventArgs.Empty) 'instead of Mybase.Text = value
End Set
End Property
To force the event and then trigger the label resize.
So it looks as solved, but if anyone has an explanation, I'd be happy to know it.

VB .Net - ShouldSerialize function

I have a problem with ShouldSerialize function.
I defined an usercontrol with a label (named Label1) on it.
Now i put the code below in the usercontrol class :
Imports System.ComponentModel
Public Class UserControl1
Dim _Range As UShort = 100
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = _Range
End Sub
Public Function ShouldSerializeTBValueRange() As Boolean
Return _Range <> 200
End Function
Public Sub ResetTBValueRange()
_Range = 200
Label1.Text = _Range
End Sub
Public Property TBValueRange As UShort
Get
Return _Range
End Get
Set(ByVal Steps As UShort)
_Range = Steps
Label1.Text = _Range
End Set
End Property
End Class
Now in a new form include the usercontrol.
In the properties grid of the usercontrol u can find the property TBValueRange.
If you right click in the property name you can Reinit the property.
After reinit u can see value 200 in the property.
Now, regenerate the project will reset the property to the initial value (id 100).
Why the value 200 didn't stay?
If i replace the line Return _Range <> 200 in the function ShouldSerializeTBValueRange() by
Return Not _Range.Equals(200)
it will work.
i don't understand.
Anyone could explain this?
The methods are working as coded. The thing which seems to not work as expected is the Not _Range.Equals(200) portion.
You initialize _Range to 100: Dim _Range As UShort = 100
Then, your ShouldSerializeTBValueRange method tells VS to only save the range value when it is not 200: Return _Range <> 200
So, when you reset it to 200 in the IDE, it wont save a value of 200, and the initial value of 100 displays.
The code uses one value for the default, 100 but use a different value for ShouldSerialize test. You should either change it to use a default of 200:
Dim _Range As UShort = 200
Or change the ShouldSerialize test to use 100:
Public Function ShouldSerializeTBValueRange() As Boolean
Return _Range <> 100
End Function
Treating the default value as the default, and using only one value for the default everything works as expected using just ShouldSerializexxx and Resetxxx with no need for anything else.
' THIS is the default value -
' ie the value that need not be saved because the
' control automatically starts with that value
Dim _Range As UShort = 200US
' Controls:
' - displays the prop value in Bold in the property window
' when the value is NOT the default
' - saves the value when it is NOT the default
' - enables/disables the RESET function
Public Function ShouldSerializeTBValueRange() As Boolean
Return (_Range <> 200US)
End Function
Using one value for the actual default (100) and then returning T/F based on a different value (200) results in
100 being saved when it need not be
200 not being saved when it should be
the Reset menu item and Bold value being in the incorrect state
The oddity is that Not _Range.Equals(200) seems to fail. It returns False after a reset (to 200) which causes the value to be saved when it really should not. There are 2 overloads for this:
UInt16.Equals(obj As Object) As Boolean
UInt16.Equals(v As UShort) As Boolean
If you pass anything other than an actual UInt16/UShort, the value will be boxed as Object. So, Not _Range.Equals(200) is using the first because 200 is Int32. The ILCode for that version is:
public override bool Equals(object obj)
{
return obj is ushort && this == (ushort)obj;
}
The test will fail the first test because obj contains an Int32. If you pass a UShort, it will work:
UShort.Equals(_Range, 200US)
'or:
_Range.Equals(200US)
All good reasons to understand the different data types, avoid boxing (As Object) and always and forever set Option Strict On.
Thanks you all to have spent time on my question.
I found my answer : when ShouldSerialize function return false the property value isn't save in the file Form1.designer.vb, so it gets the original value.
For my needed the ShouldSerialize function should always return true.
Public Function ShouldSerializeTBValueRange() As Boolean
Return true
End Function
So the property value will always be saved.
Now, what i'm looking for is how to control the context menu of the property grid.
I want the option "Reinit" greyed when the value is good.
Finally, i found something, with the use of PropertyDescriptor.
For whose are interested, we can save properties in the designer.vb file and test the value to reset.
here is my program, based on the article from MSDN - Microsft:
Imports System.ComponentModel
Imports System.Windows.Forms.Design
<Designer(GetType(DemoControlDesigner))>
Public Class UserControl1
Dim _MyProperty1 As String = "Test1"
Dim _MyProperty2 As Integer = 100
Dim _MyProperty3 As UShort = 200
Public Sub New()
InitializeComponent()
End Sub
Public Function CanResetMyProperty1() As Boolean
Return _MyProperty1 <> "Test"
End Function
Public Sub ResetMyProperty1()
_MyProperty1 = "Test"
End Sub
Public Property MyProperty1 As String
Get
Return _MyProperty1
End Get
Set(ByVal Value As String)
_MyProperty1 = Value
End Set
End Property
Public Function CanResetMyProperty2() As Boolean
Return _MyProperty2 <> 150
End Function
Public Sub ResetMyProperty2()
_MyProperty2 = 150
End Sub
Public Property MyProperty2 As Integer
Get
Return _MyProperty2
End Get
Set(ByVal Value As Integer)
_MyProperty2 = Value
End Set
End Property
Public Function CanResetMyProperty3() As Boolean
Return _MyProperty3 <> _MyProperty2
End Function
Public Sub ResetMyProperty3()
_MyProperty3 = _MyProperty2
End Sub
Public Property MyProperty3 As UShort
Get
Return _MyProperty3
End Get
Set(ByVal Value As UShort)
_MyProperty3 = Value
End Set
End Property
End Class
Friend NotInheritable Class SerializePropertyDescriptor
Inherits PropertyDescriptor
Private _pd As PropertyDescriptor = Nothing
Public Sub New(ByVal pd As PropertyDescriptor)
MyBase.New(pd)
_pd = pd
End Sub
Public Overrides ReadOnly Property Attributes() As AttributeCollection
Get
Return Me._pd.Attributes
End Get
End Property
Protected Overrides Sub FillAttributes(ByVal attributeList As IList)
MyBase.FillAttributes(attributeList)
End Sub
Public Overrides ReadOnly Property ComponentType() As Type
Get
Return Me._pd.ComponentType
End Get
End Property
Public Overrides ReadOnly Property Converter() As TypeConverter
Get
Return Me._pd.Converter
End Get
End Property
Public Overrides Function GetEditor(ByVal editorBaseType As Type) As Object
Return Me._pd.GetEditor(editorBaseType)
End Function
Public Overrides ReadOnly Property IsReadOnly() As Boolean
Get
Return Me._pd.IsReadOnly
End Get
End Property
Public Overrides ReadOnly Property PropertyType() As Type
Get
Return Me._pd.PropertyType
End Get
End Property
Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
Try
Return CallByName(component, "CanReset" & _pd.Name, CallType.Get, Nothing)
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return False
End Function
Public Overrides Function GetValue(ByVal component As Object) As Object
Return Me._pd.GetValue(component)
End Function
Public Overrides Sub ResetValue(ByVal component As Object)
Me._pd.ResetValue(component)
End Sub
Public Overrides Sub SetValue(ByVal component As Object, ByVal val As Object)
Me._pd.SetValue(component, val)
End Sub
Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
Return True
End Function
End Class
Class DemoControlDesigner
Inherits ControlDesigner
Dim PropertiesToSerialize As String() = {"MyProperty1", "MyProperty2", "MyProperty3"}
Protected Overrides Sub PostFilterProperties(ByVal properties As IDictionary)
Dim original As PropertyDescriptor
For Each PropName As String In PropertiesToSerialize
If properties.Contains(PropName) Then
original = properties(PropName)
properties(PropName) = New SerializePropertyDescriptor(original)
End If
Next
MyBase.PostFilterProperties(properties)
End Sub
End Class
For each property that should be serialized and tested for reset, we should write a sub "CanResetPropertyName" which test the value to reset (see sample).
And the values stay now, even if we regenerate the project.
It's working fine for me, maybe it can be improved.
Regards.

VB.NET Forms ListBox doesn't display DataSource

I'm trying to link my ListBox to an ObservableCollection.
Here's my class for defining mods:
Public Class TroveMod
Private m_FileName As String
Private m_Enabled As Boolean
Public Property FileName() As String
Get
Return m_FileName
End Get
Set(value As String)
m_FileName = value
End Set
End Property
Public Property Enabled() As Boolean
Get
Return m_Enabled
End Get
Set(value As Boolean)
m_Enabled = value
End Set
End Property
Public ReadOnly Property ModName()
Get
Return Path.GetFileNameWithoutExtension(FileName)
End Get
End Property
End Class
And this is the actual Property ModList:
Public Property ModList() As ObservableCollection(Of TroveMod)
Get
Return m_ModList
End Get
Set(value As ObservableCollection(Of TroveMod))
m_ModList = value
End Set
End Property
I add items using:
Private Sub AddMod(file__1 As String, enabled As Boolean)
If File.Exists(file__1) Then
ModList.Add(New TroveMod() With { _
.FileName = file__1, _
.Enabled = enabled _
})
End If
End Sub
Everytime I want to add something to this Collection using AddMod, it won't show off in my listbox :/ I added an ModListBindingSource to the ListBox and set the DisplayMember and ValueMember to ModName but it still won't work. I also got a status label, which says, that it successful added the mods to the collection but it simply won't show them in the ListBox. Did I miss something?
An ObservableCollection is not quite what you want - this would allow your code to see changes to the collection via events. For instance, if there were several actors adding items to the collection and you wanted to know that. In this case, since 'local' code is adding items, it is not needed.
It is also not clear how the collection is mapped to the ListBox as the DataSource. Try this:
Public Class Form...
Private myList As New BindingList(Of TroveMod)
Sub Form_Load(....
theListBox.DataSource = myList
theListBox.DisplayMember = "ModName"
Now, as you add things to the BindingList they will appear in theListBox. If an item changes though (e.g. the Name), that change will not show up without a little more work, but as these are file names that seems unlikely.
The form property is not needed unless an external class also needs access to the collection/BindingList; in that case, you probably do not want need a setter.

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

.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.