Outside the class I'd like to be able to access CCTV.Camera.Brightness property as ReadOnly, but methods within the CCTV class should be able to modify the Brightness property. Please can you advise on how to achieve this?
I think I need to add an Interface that exposes some properties and hides others, but I'm not sure of the implementation. [Note the constructor and main sub are obviously contrived for this example and testing].
Public Class CCTV
Public Class Camera
Public Property Name as String
Public Property Brightness as Integer
End Class
Dim cameras as New Dictionary(Of String, Camera)
Public Sub New()
Dim cam As New Camera
cam.Name = "driveway"
cam.Brightness = 5
cameras.Add(cam.Name, cam)
End Sub
Public Sub ChangeBrightness(value as Integer)
cameras("driveway").Brightness = value
End Sub
End Class
Sub main()
Dim MyCCTV = new CCTV
MyCCTV.ChangeBrightness(10)
if MyCCTV("driveway").Brightness = 10 then Console.Write("Brightness is 10")
End Sub
Get getter and the setter of a property can have different accessibility modifiers. In this case you want Brightness to be readable by everybody but only the code you trust should be able to write it. You do so like this:
Public Class Camera
Private _brightness As Integer
Public Property Brightness As Integer
Get
Return _brightness
End Get
Friend Set(value As Integer)
_brightness = Value
End Set
End Property
'' etc...
End Class
Note the added Friend keyword, it limits access to the code in the same project that Camera class is a part of. It can also be Private or Protected if you want to limit access to only code inside the Camera class or its derived classes.
no interface is need in this case.You simply have to create your property as readonly.when you set the property as readonly assume that the value will stored within a private variable and at that point should be better pass it or to a method or to the subnew using overloads methods let me show you an example:
Public Class CCTV
Public Class Camera
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Brightness As String
Public ReadOnly Property Brightness() As String
Get
Return _Brightness
End Get
End Property
''' <summary>
''' Defautl sub new method
''' </summary>
''' <remarks></remarks>
Sub New()
End Sub
''' <summary>
''' second overload
''' </summary>
''' <param name="cameraName"></param>
''' <param name="brightness"></param>
''' <remarks></remarks>
Sub New(ByVal cameraName As String, ByVal brightness As Integer)
Me.Name = cameraName
Me._Brightness = brightness
End Sub
''' <summary>
''' Change brigthness
''' </summary>
''' <param name="value"></param>
''' <remarks></remarks>
Public Sub setUpCameraBrightness(ByVal value As Integer)
'take care to use private varibale so it will reflcet changes into propetry readonly
Me._Brightness = value
End Sub
End Class
Dim cameras As New Dictionary(Of String, Camera)
Public Sub New()
'two differnet approach
'first overaload:
Dim cam As New Camera()
cam.Name = "yourcamname"
cam.setUpCameraBrightness(10)
cameras.Add(cam.Name, cam)
''second approch declarative value:
Dim cam2 As New Camera("yourcamname", 10)
cameras.Add(cam2.Name, cam2)
End Sub
Public Sub ChangeBrightness(value As Integer)
cameras("driveway").setUpCameraBrightness(100)
End Sub
End Class
Sub main()
Dim MyCCTV = New CCTV
MyCCTV.ChangeBrightness(10)
If MyCCTV("driveway").Brightness = 10 Then Console.Write("Brightness is 10")
End Sub
Related
I have a primitive Class that looks like this:
Public Class BaseGeoData
Property GeoOrigin As String
Property GeoDestination As String
Property TravelDistance As Double?
Property TravelTime As Double?
Public Sub New()
End Sub
End Class
Public Class GeoData
Inherits BaseGeoData
Public Sub New(geoOrigStr As String, geoDestStr As String)
GeoOrigin = geoOrigStr
GeoDestination = geoDestStr
TravelDistance = 5000 'in meters
TravelTime = 360 'in minutes
End Sub
End Class
I want to be able to add 2 extensions that will return converted values like this:
TravelDistance.ToMiles()
TravelTime.ToHours()
When I add a Module to extend the class, it offers the extension to the entire class, most properties of which will never use the extension. How can I just offer the extensions to the properties that need them?
Introduce own type of "Unit" for measurement values
Public MustInherit Class Unit
Public ReadOnly Property Value As Double
Public MustOverride ReadOnly Property Name As String
Public Sub New(value As Double)
Me.Value = value
End Sub
Public Overrides Function ToString() As String
Return $"{Value} {Name}"
End Function
End Class
Public Class Meter
Inherits Unit
Public Sub New(value As Double)
MyBase.New(value)
End Sub
Public Overrides ReadOnly Property Name As String
Get
Return "m"
End Get
End Property
End Class
Public Class Mile
Inherits Unit
Public Sub New(value As Double)
MyBase.New(value)
End Sub
Public Overrides ReadOnly Property Name As String
Get
Return "mi"
End Get
End Property
End Class
And extension methods for creating unit and convertions
Public Module UnitConversions
<Extension>
Public Function Meters(value As Integer) As Meter
Return New Meter(value)
End Function
<Extension>
Public Function Miles(value As Integer) As Mile
Return New Mile(value)
End Function
<Extension>
Public Function ToMiles(meters As Meter) As Mile
Dim miles = meters.Value * 0.00062137
Return New Mile(miles)
End Function
<Extension>
Public Function ToMeters(miles As Mile) As Meter
Dim meters = miles.Value * 1609.344
Return New Meter(meters)
End Function
End Module
Then you can use value in more readable manner
TravelDistance = 5000.Meters() ' meters
' Conversion
geoData.TravelDistance.ToMiles() ' miles
Console.WriteLine(geoData.TravelDistance) ' print 3.10685 mi
You can only add extension methods into types (i.e. classes).
TravelDistance is of type Double? so you have to add an extention method into Double?.
Note that it would make the method available for every Double?, which may not be something you want.
I really like Plutonix's resolution and is the same one I would go for first.
Its simple and resolves your initial problem.
Public Class BaseGeoData
Property GeoOrigin As String
Property GeoDestination As String
Property TravelDistance As Double?
Property TravelTime As Double?
Public Sub New()
End Sub
End Class
Public Class GeoData
Inherits BaseGeoData
Public Sub New(geoOrigStr As String, geoDestStr As String)
GeoOrigin = geoOrigStr
GeoDestination = geoDestStr
TravelDistance = 5000 'in meters
TravelTime = 360 'in minutes
End Sub
Function DistanceMiles() As Double
DistanceMiles = (TravelDistance/1609.344)
End Function
Function TimeHours() As Double
DistanceMiles = (TravelTime /60)
End Function
End Class
I'm working on a vb.net project where a user can save and open a binary text.
However, the problem is that the when opening a file, it returns a object instead of string and integer.
It returns Projectname.Classname
What I want it to return is name and age that the user entered when saving the file.
Here is my code:
''' <summary>
''' When the user clicks open
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
Try
If (openFileDialog1.ShowDialog = DialogResult.OK) Then
thefilename = openFileDialog1.FileName
Dim message = animalmgr.ReadFile(thefilename)
'Getting method from manager
If (Not (message) Is Nothing) Then
message.ToList().ForEach(Sub(msg) Resultlst.Items.Add(msg))
Else
UpdateResults()
End If
End If
Catch ex As Exception
MessageBox.Show(ex.ToString, "Error in opening file")
End Try
End Sub
ReadFile function in the manager class:
''' <summary>
''' Reads a file from binary
''' </summary>
''' <param name="filename"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function ReadFile(ByVal filename As String) As Animal()
Dim BinSerial As BinSerializerUtility = New BinSerializerUtility
Dim animals = BinSerial.BinaryFileDeSerialize(Of Animal)(filename)
Return animals.ToArray
End Function
I did the exact same project in C# which worked perfectly, I don't know why it doesn't work in vb.net however. Does anyone know the problem and how to solve it?
Update:
Class BinSerializerUtility
Public Class BinSerializerUtility
Public Sub BinaryFileSerialize(ByVal objs As List(Of Animal), ByVal filePath As String)
Dim fileStream As FileStream = Nothing
Try
fileStream = New FileStream(filePath, FileMode.Create)
Dim b As BinaryFormatter = New BinaryFormatter
For Each obj In objs
b.Serialize(fileStream, obj)
Next
Finally
If (Not (fileStream) Is Nothing) Then
fileStream.Close()
End If
End Try
End Sub
Public Function BinaryFileDeSerialize(Of T As {Class})(ByVal filePath As String) As List(Of T)
Dim list = New List(Of T)
If Not File.Exists(filePath) Then
Throw New FileNotFoundException(("The file" + " was not found. "), filePath)
End If
Dim fileStream = New FileStream(filePath, FileMode.Open)
Dim b As BinaryFormatter = New BinaryFormatter
While (fileStream.Position < fileStream.Length)
list.Add(CType(b.Deserialize(fileStream), T))
End While
Return list
End Function
End Class
Animal class:
<Serializable()>
Public MustInherit Class Animal
Implements IAnimal
Private theCatagorytype As Categorytype
Private m_name As String
Private m_age As Integer
Private m_gender As String
Private m_id As Integer
Public Sub New(ByVal typ As Categorytype)
MyBase.New()
theCatagorytype = typ
End Sub
Public Sub New()
MyBase.New()
End Sub
#Region "Properties"
Public Overridable ReadOnly Property Description As String
Get
Return String.Format("{0}, {1}, {2}, {3}", Me.Id, Me.Name, Me.Age, Me.Gender)
End Get
End Property
Public Property Categorytype As Categorytype
Get
Return theCatagorytype
End Get
Set(value As Categorytype)
theCatagorytype = value
End Set
End Property
Public Property Id As Integer Implements IAnimal.Id
Get
Return m_id
End Get
Set(value As Integer)
If (value > 0) Then
m_id = value
End If
End Set
End Property
Public Property Name As String Implements IAnimal.Name
Get
Return m_name
End Get
Set(value As String)
m_name = value
End Set
End Property
Public Property Age As Integer
Get
Return m_age
End Get
Set(value As Integer)
m_age = value
End Set
End Property
Public Property Gender As String Implements IAnimal.Gender
Get
Return m_gender
End Get
Set(value As String)
m_gender = value
End Set
End Property
#End Region
Public MustOverride Function GetEaterType() As EaterType Implements IAnimal.GetEaterType
Public MustOverride Function GetSpecies() As String Implements IAnimal.GetSpecies
End Class
Rather than writing a class wrapper for an operation, I think it makes more sense to put such Save and Load functions in the class itself:
' in Animal class:
Friend Shared Function LoadData(myFile As String) As List(Of Animal)
Dim newList As List(Of Animal)
If File.Exists(myFile) Then
Using fs As New FileStream(myFile, FileMode.Open)
Dim bf = New BinaryFormatter
newList = CType(bf.Deserialize(fs), List(Of Animal))
End Using
Else
'return empty list of there is no file
newList = New List(Of Animal)
End If
Return newList
End Function
This seems simpler than trying to read one object at a time and place it in a List. Organizationally, it makes for fewer utility classes if the Foos, Bars and Animals can all serialize and deserialize themselves. To load them:
animals = Animal.LoadData(FileName)
' put animals in a ListBox:
myfrm.ListBox1.Items.AddRange(animals.ToArray)
Even better than making a copy of the animals for the list box, is to use your List(Of Animal) as the DataSource. That way, the user sees the same data set as your code is using.
When adding objects to a listbox or dropdown list, by default it uses ToString() to give you a string representation of the object, and the class name is the default ToString() implementation.
If you override ToString() in your Animal class, it will display however you need it to.
EDIT This is assuming you really want your ReadFile method to return a collection of Animal objects, and you want to add the actual objects to the listbox instead of truly adding string representations to the listbox.
I'm using a class that Implements from an interface.
I want to implement a property from the interface but I'm getting error:
Class 'ListManager' must implement 'Property Count As Integer' for interface 'IListManager(Of T)'. Implementing property must have matching 'ReadOnly' or 'WriteOnly' specifiers.
Interface:
Namespace Assign_1
Public Interface IListManager(Of T)
''' <summary>
''' Return the number of items in the collection m_list
''' </summary>
Property Count As Integer
End Namespace
Class:
Namespace Assign_1
Public Class ListManager(Of T)
Implements IListManager(Of T)
Protected m_list As List(Of T)
''' <summary>
''' Constructor
''' </summary>
Public Sub New()
MyBase.New()
m_list = New List(Of T)
End Sub
''' <summary>
''' Property to count the list
''' </summary>
Public ReadOnly Property Count As Integer
Get
Return m_list.Count
End Get
End Property
End Namespace
Does anyone know the problem?
It shouldn't be necessary to define the Interface as a generic since an interface just establishes a contract of which properties and methods are implemented.
Public Interface IListManager
ReadOnly Property Count As Int32
' function version
Function GetCount() As Integer
End Interface
Public Class ListMgr(Of T)
Implements IListManager
Private myList As List(Of T)
Public ReadOnly Property Count As Integer Implements IListManager.Count
Get
Return myList.Count
End Get
End Property
Public Function GetCount() As Integer Implements IListManager.GetCount
Return myList.Count
End Function
End Class
If you did need the interface to be generic there is just a small change:
Public ReadOnly Property Count As Integer Implements IListManager(Of T).Count
After you type in Implements IMyInterface VS will add the boilerplate property and method code when you press enter.
This works for me
Public Interface IListManager(Of T)
ReadOnly Property count As Integer
End Interface
Public Class ListManager(Of T) : Implements IListManager(Of TabAlignment)
Protected m_list As List(Of T)
''' <summary>
''' Constructor
''' </summary>
Public Sub New()
MyBase.New()
m_list = New List(Of T)
End Sub
Public ReadOnly Property count As Integer Implements IListManager(Of TabAlignment).count
Get
Return Me.m_list.Count
End Get
End Property
End Class
(this is not related to VB6)
like above, I want to make a 'textbox' control between separated 'label' controls.
I used method to load those three controls (two labels and one textbox), but I cannot precisely locate the textbox and the following label.
In the application, there will be more than hundreds of sentences with almost the same number of blanks(textbox), so the perfect lining is required.
So, is there any good method to do that?
Thank you!
You can make a class that inherits from Panel and includes two Labels and a Textbox that get automatically realigned. I did something like that to get you started. Just paste the code into your project, recompile once and you can add these to your Form from the Toolbox (or create them dynamically of course). This is nowhere complete. You should make properties for the fonts for example and additional events based on your needs but you get my idea I think.
Public Class ctrlBlankSentence
Inherits Panel
Private WithEvents lblLeft As Label 'The left part of the sentence
Private WithEvents tbBlank As TextBox 'The blank text
Private WithEvents lblRight As Label 'The right part of the sentence
Public Event BlankTextChanged() 'Use events to react to user input
Private _Spacing As Integer
''' <summary>
''' Determines the distance between the labels and the textbox
''' </summary>
Public Property Spacing As Integer
Get
Return _Spacing
End Get
Set(value As Integer)
_Spacing = value
DoreAlign()
End Set
End Property
Private _TextboxWidth As Integer
''' <summary>
''' Determines the width of the textbox
''' </summary>
Public Property TextboxWidth As Integer
Get
Return _TextboxWidth
End Get
Set(value As Integer)
_TextboxWidth = value
DoreAlign()
End Set
End Property
Private _AutosizeControl As Boolean
''' <summary>
''' Determines if the panel is automatically resized.
''' </summary>
Public Property AutosizeControl As Boolean
Get
Return _AutosizeControl
End Get
Set(value As Boolean)
_AutosizeControl = value
DoreAlign()
End Set
End Property
''' <summary>
''' Used to get or set the left part of the sentence
''' </summary>
Public Property LeftText As String
Get
Return lblLeft.Text
End Get
Set(value As String)
lblLeft.Text = value
DoreAlign()
End Set
End Property
''' <summary>
''' Used to set the right part of the sentence
''' </summary>
Public Property RightText As String
Get
Return lblRight.Text
End Get
Set(value As String)
lblRight.Text = value
DoreAlign()
End Set
End Property
''' <summary>
''' Used to get or set the user input
''' </summary>
Public Property BlankText As String
Get
Return tbBlank.Text
End Get
Set(value As String)
tbBlank.Text = value
RaiseEvent BlankTextChanged()
End Set
End Property
#Region "Constructor"
Public Sub New()
lblLeft = New Label With {.AutoSize = True, .Text = "Left Text"}
lblRight = New Label With {.AutoSize = True, .Text = "Right Text"}
tbBlank = New TextBox
Spacing = 3
TextboxWidth = 100
AutosizeControl = True
Me.Controls.Add(lblLeft)
Me.Controls.Add(lblRight)
Me.Controls.Add(tbBlank)
DoreAlign()
End Sub
#End Region
#Region "The method the realigns the controls"
Private Sub DoreAlign()
lblLeft.Left = 0
tbBlank.Left = lblLeft.Right + Spacing
tbBlank.Width = TextboxWidth
lblRight.Left = tbBlank.Right + Spacing
If AutosizeControl Then
Me.Width = lblRight.Right
Me.Height = tbBlank.Height
End If
lblRight.Top = CInt(Me.Height / 2 - lblRight.Height / 2)
lblLeft.Top = lblRight.Top
End Sub
#End Region
Private Sub thisTextChanged() Handles tbBlank.TextChanged
RaiseEvent BlankTextChanged()
End Sub
End Class
If have got the following definitions of constants:
Protected Const Xsl As String = "Configuration.Xsl"
Protected Const Form As String = "Settings.Form"
Protected Const Ascx As String = "Implementation.Ascx"
...
To fill a dictionary I use this constants as keys:
MyDictionary.Add(Converter.Xsl, "Item 1")
MyDictionary.Add(Converter.Form, "Item 2")
MyDictionary.Add(Converter.Ascx, "Item 3")
...
Now I run throug a loop of XML files and extract the name of the root node:
Dim document As New XmlDocument
document.Load(File.FullName)
Dim rootName As String = document.DocumentElement.Name
The root name matchs with the name of the constant. To get the value of an item from the dictionary I can use something like this:
Select Case rootName.ToUpper
Case "Xsl".ToUpper
DictionaryValue = MyDictionary(Class.Xsl)
Case "Form".ToUpper
DictionaryValue = MyDictionary(Class.Form)
Case "Ascx".ToUpper
DictionaryValue = MyDictionary(Class.Ascx)
...
Case Else
End Select
If a constant is added or removed I also have to change the selection. Is there another way to get the value of a constant? Something like
DictionaryValue = MyDictionary(SomeFunctionToGetConstantValue(rootName))
Thanks for any response.
Try this:
For Each sKey As String In MyDictionary.Keys
If rootName.Equals(sKey, StringComparison.CurrentCultureIgnoreCase) Then
DictionaryValue = MyDictionary(sKey)
Exit For
End If
Next
At least it will reduce the amount of coding in the select case.
#Clara Onager
My solution I used was the following
Me.GetType.GetField(
"Xsl",
Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static Or System.Reflection.BindingFlags.FlattenHierarchy
).GetValue(Nothing)
This is a bit of a whopper but I think it provides a pretty elegant solution overall. This is how it is used:
Public Class ConstantsExample
Public Sub UseConstant()
Dim value As String = Constants.Types(TypeA)
Dim category As String = Constants.Categories(General)
End Sub
End Class
As you can see the code where you use it is as short as it can be made. It does rely on a big pile of source code though:
Public Enum TypeCodes
<Description("Type A")> TypeA = 0
<Description("Type B")> TypeB
<Description("Type C")> TypeC
End Enum
Public Enum CategoryCodes
<Description("General")> General = 0
<Description("Specific")> Specific
<Description("Other")> Other
End Enum
Public NotInheritable Class Constants
#Region "Resources"
Private Shared myTypes As Dictionary(Of TypeCodes, ConstantItem) = Nothing
Public Shared ReadOnly Property Types() As Dictionary(Of TypeCodes, ConstantItem)
Get
If myTypes Is Nothing Then
myTypes = New Dictionary(Of TypeCodes, ConstantItem)
BuildTypes(myTypes)
End If
Return myTypes
End Get
End Property
Private Shared Sub BuildTypes(ByRef dict As Dictionary(Of TypeCodes, ConstantItem))
With dict
.Add(TypeCodes.TypeA, New ConstantItem(TypeCodes.TypeA.Description, "Type A are..."))
.Add(TypeCodes.TypeB, New ConstantItem(TypeCodes.TypeB.Description, "Type B are..."))
.Add(TypeCodes.TypeC, New ConstantItem(TypeCodes.TypeC.Description, "Type C are..."))
End With
End Sub
#End Region
#Region "Categories"
Private Shared myCategories As Dictionary(Of CategoryCodes, ConstantItem) = Nothing
Public Shared ReadOnly Property Categories() As Dictionary(Of CategoryCodes, ConstantItem)
Get
If myCategories Is Nothing Then
myCategories = New Dictionary(Of CategoryCodes, ConstantItem)
BuildCategories(myCategories)
End If
Return myCategories
End Get
End Property
Private Shared Sub BuildCategories(ByRef dict As Dictionary(Of CategoryCodes, ConstantItem))
With dict
.Add(CategoryCodes.General, New ConstantItem(CategoryCodes.General.Description, "General category"))
.Add(CategoryCodes.Specific, New ConstantItem(CategoryCodes.Specific.Description, "Specific category"))
.Add(CategoryCodes.Other, New ConstantItem(CategoryCodes.Other.Description, "Other category"))
End With
End Sub
#End Region
End Class
Public NotInheritable Class ConstantItem
#Region "Constructors"
''' <summary>
''' Default constructor.
''' </summary>
Public Sub New()
'Do nothing
End Sub
''' <summary>
''' Simple constructor.
''' </summary>
Sub New(value As String)
Me.Name = value
Me.Description = value
End Sub
''' <summary>
''' Proper constructor.
''' </summary>
Sub New(name As String, description As String)
Me.Name = name
Me.Description = description
End Sub
#End Region
Property Name As String
Property Description As String
''' <summary>
''' See http://stackoverflow.com/questions/293215/default-properties-in-vb-net
''' </summary>
Public Shared Widening Operator CType(value As String) As ConstantItem
Return New ConstantItem(value)
End Operator
''' <summary>
''' See http://stackoverflow.com/questions/293215/default-properties-in-vb-net
''' </summary>
Public Shared Widening Operator CType(value As ConstantItem) As String
Return value.Name
End Operator
End Class
Note the use of the Widening Operator to dispense with having to type .Item. If you'd rather not use the Widening Operator then simple comment that bit out and change Constants.Types(TypeA) to Constants.Types.Item(TypeA).
This is the Description Extension you may need:
Public Module Extensions
Private Enum SampleDescription
<Description("Item One")> ItemOne = 1
<Description("Item Two")> ItemTwo = 2
<Description("Item Three has a long description")> ItemThree = 3
End Enum
''' <summary>
''' This procedure gets the description attribute of an enum constant, if any. Otherwise it gets
''' the string name of the enum member.
''' </summary>
''' <param name="value"></param>
''' <returns></returns>
''' <remarks>Usage: myenum.Member.Description()
''' Add the Description attribute to each member of the enumeration.</remarks>
<Extension()> _
Public Function Description(ByVal value As [Enum]) As String
Dim fi As Reflection.FieldInfo = value.GetType().GetField(value.ToString())
Dim aattr() As DescriptionAttribute = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If aattr.Length > 0 Then
Return aattr(0).Description
Else
Return value.ToString()
End If
End Function
End Module
And these are the Imports statements I used (the Assembly is called MyPatterns):
Imports System.ComponentModel
Imports MyPatterns.TypeCodes
Imports MyPatterns.CategoryCodes
Importing the two 'codes' allows you to do without the prefix for the Enum which shortens the code.