How do I declare an object as public? - vb.net

VB.net gets mad when I say:
Public thisTicket as new ticket
and insists I need to use Dim instead of Public, but this prevents me from accessing the object outside of the sub I declared it in.
Basically I am trying to do the following in one sub:
Public thisTicket as new ticket
thisTicket.completed = true
and then this in another sub:
if thisTicket.completed = true then
'do this
else
'do this instead
end if
Class:
Public Class ticket
Property hasStatusChange As Boolean
Property initialAssignmentStatus As String
Property initialApprovalStatus As String
Property initialCompletionStatus As String
Property newApprovalStatus As String
Property newCompletionStatus As String
Property newAssignmentStatus As String
Property wasUpdated As Boolean
End Class
Another question,
If I made a new property under ticket called "completionChanged as boolean"
and the criteria for that being true or false would be if the initialCompletionStatus was not equal to the newCompletion status (then it would be true),
how would I get it so that I could say:
if thisTicket.completionchanged = true then
and have it know on it's own whether it's true or not, without me having to spell out the if/then each time?

A Local variable in a Sub or Function or property getter or setter (i.e. in a method) lives only while the method is being executed. Before or after the call of this method, the local variable does not exist. Therefore modifiers like Public make no sense there.
If you need to access a variable from outside a method, make it a class member (either a field or a property).
Answer to second question. This is why properties exist. You can add logic to them that is executed when they are accessed.
Private m_completionStatus As String
Property CompletionStatus() As String
Get
Return m_completionStatus
End Get
Set(ByVal Value As String)
If Value <> m_completionStatus Then
m_completionStatus = Value
completionChanged = True
End If
End Set
End Property
And then please write
If thisTicket.completionchanged Then
and drop the " = true ". What the If-statement wants is an expression yielding a Boolean result, and a Boolean variable is such an expression in itself. There is not need to have any comparison in an If-statement.
If completionchanged is True then completionchanged = True is True also. If completionchanged is False then completionchanged = True is False also. Comparing with "= True" is a bit like multiplying a number with 1. It doesn't change anything.

If you would like it to be a member of the class, such as a property.
Public Property ThisTicket As Ticket
And in your class' constructor initialize it.
Public Sub New()
ThisTicket = new Ticket
End Sub
Now you will be able to access this ticket from inside of your class. However, consider whether other places in your code really need to know about this member. If they do not, make it private. If you may extend this class in the future where the subtypes would need access, make it protected.
If you decide to make it private, you can bypass making it a property and just create it as a field.
Private _thisTicket As Ticket
You should also ask yourself if thisTicket really belongs to the state of the containing object. Does the containing object process a lot of tickets? Maybe the ticket should be passed into the processing function instead of being contained. If the containing object's state does concern itself with only that one ticket throughout its life, then making it a property is valid.
As for your second question..
Create a function in Ticket to evaluate the business logic you mentioned and return the boolean value for if it has changed or not.
Public Function HasStatusChanged As Boolean
logic...
End Function

Related

What do the Get and Set commands in Visual Basic do?

What do the Get and Set commands in Visual Basic do? I encountered the Get and Set commands in my book's chapter on Object-Oriented Programming. Here is some code:
Public Property SocSecNum() As String
Get
Return m_ssn
End Get
Set(value As String)
m_ssn = value
End Set
End Property
When you have a field, i.e. a member variable, you can get its value:
var = someObject.SomeField
or you can set its value:
someObject.SomeField = someValue
The whole point of a property is that, from the outside, it behaves just like a field but, from the inside, it behaves like methods. That means that you can get and set a property just like you can a field but, on the inside, rather than just straight assignment and retrieval, it actually executes the Get and Set part of the property definition.
When your project is compiled, they are actually implemented as methods and the property value is stored elsewhere. That might be a dedicated field or it might not. An example of the latter is the Lines property of a TextBox. The is no dedicated field for that data. What actually happens is that the Get part takes the current text, splits it on line breaks and returns the resulting array. Similarly, the Set part combines the array provided into a single String and sets the Text. Two of the most common reasons for extra code are validation and raising events.
A simple field declaration looks like this:
Public SomeField As SomeType
A fully-implemented property looks like this:
Private someField As SomeType
Public Property SomeProperty As SomeType
Get
Return someField
End Get
Set(value As SomeType)
someField = value
End Set
End Property
As you can see, the field is private and, when the property is invoked in code, the getter and setter will get or set the value of that field. Once compiled, that code actually looks like this:
Private someField As SomeType
Public Function get_SomeProperty As SomeType
Return someField
End Function
Public Sub set_SomeProperty(value As SomeType)
someField = value
End Sub
As you can see, the getter and setter are literally methods and the property is just syntactic sugar. Java doesn't have properties and Java developers literally write these two methods as accessors for a field.
More recently, the authors of VB have realised that it's laborious to write all that code out over and over for lots of properties so we now have auto-properties. You can simply write this:
Public Property SomeProperty As SomeType
and everything else is implied. There's still a backing field and there is still a getter and a setter but your code is not cluttered up by showing them. This is how you will write most properties these days. As I said before, you'll still need to write the property out in full if you want to add any extra functionality to the getter or setter. Raising a changed event is probably the most common example of that, e.g.
Public Event SomePropertyChanged As EventHandler
Private someField As SomeType
Public Property SomeProperty As SomeType
Get
Return someField
End Get
Set
If someField <> value Then
someField = value
OnSomePropertyChanged(EventArgs.Empty)
End If
End Set
End Property
Protected Overridable Sub OnSomePropertyChanged(e As EventArgs)
RaiseEvent SomePropertyChanged(Me, e)
End Sub
In this case, when the property is set, the backing field is set if and only if the new value is different to the old value and then an event is raised to notify listeners of that change in property value. The fact that properties can include extra code like this is why they should be used over fields for public data.
On the Tools menu select Options. Click on Debugging. Uncheck Step over properties and operators
Create your class thusly. It can be nested in the form class for this little test.
Public Class SomeClass
Private m_ssn As String
Public Property SocSecNum() As String
Get
Return m_ssn
End Get
Set(value As String)
m_ssn = value
End Set
End Property
End Class
Now, in the Button.Click create an instance of your class. Set the SocSecNum. Then Get the value and assign it to a text box.
Next set a break point in your code on the first line of the button code. Do this by positioning your cursor on the gray line to the left of the code and clicking.
Run your program and click the button. You will encounter the break point. You can now step through your code line by line. Use the Step into symbol.
You will see that when you set the property the Property Procedures Set is called. The value is stored in the Private m_ssn variable. Likewise, when you get the property the value of the private variable is returned. Hold your cursor over the m_ssn variable when you are in the Property Procedure and you will see the value stored there.
All this boilerplate code disappears in current versions of Visual Studio where the default Getter, Setter and private member are created for you behind the scenes with auto properties. You can still write the Get and Set and private variable when you need to do things to the input or output.
Here's a little that can give you an idea
I used this if to easily disable some buttons while process is running
and then when you call the "decrypting = false" the disabled buttons will become enabled
Dim dec As Boolean
Private Property decrypting As Boolean
Get
Return dec
End Get
Set(value As Boolean)
dec = value
partialFile = False
btnAddFiles.Enabled = Not dec
btnAddFolder.Enabled = Not dec
btnAddKeyFile.Enabled = Not dec
btnSaveAs.Enabled = Not dec
txtOutputFileName.Enabled = Not dec
txtOutputFolder.Enabled = Not dec
txtInputFilePath.Enabled = Not dec
txtKeyFilePath.Enabled = Not dec
LV.Enabled = Not dec
btnDecrypt.Enabled = Not dec
btnAbort.Enabled = dec
If Not decrypting Then
Me.Text = "TS Decrypter and Joiner v1.0"
End If
End Set
End Property

VBA: Use of Public Property Get in place of Const (for Non-Unicode Characters)

I have a VBA code where I need to define a constant string containing Non-Unicode characters (£). As some might know, VBA Editor doesn't support non-unicode and uses windows "System Locale" setting in regional and language setting to parse/map these characters. The machine I develop the code is set to English system Locale but some of the users have that setting as other languages, e.g. Chinese which turns the string constant to question mark (£ --> ?).
Now, £ = chr(163), however, you cannot use chr as part of defining a constant in VBA. So while this is allowed:
public const mystring = "reference constant string with £"
this is not allowed in VBA"
public const mystring = "reference constant string with " & chr(163).
One way around is to define mystring as a public/global variable:
Constants.bas
public mystring as string
and then set the public variable on start of running code or Excel opening.
ThisWorkbook
Private Sub Workbook_Open()
mystring = "reference constant string with " & chr(163).
End Sub
One issue with this process is the public variables get cleared when error happens or the code stops. To keep the value an alternate I came across was to avoid public variable and instead use a public property get. Note that I have to include this a part of a class.
**.cls
Public Property Get mystring () As String
mystring = "\R;;" & Chr(163)
End Property
So, now I am wondering if there will be any issue with this approach? or perhaps there is a better approach to address the constant variable with non-unicode character.
The main issue is the name of the module, Constants - it's misleading, since neither a public/global variable nor a public get-only property are constants.
Side node, constants not being assignable to non-constant expressions isn't a limitation that's specific to VBA.
Properties are perfectly legal in standard modules, and public get-only properties are a perfectly fine way to expose a read-only value that needs to be constructed at run-time.
the public variables get cleared when error happens or the code stops
Assuming "when error happens" involves clicking End and effectively ending the execution context, that is true of everything exposed anywhere, whether it's a global variable, a public property, an object, or anything that exists in-memory at run-time... and that's just normal stuff - the value is simply available on-demand any time.
Again there's no need to have a class module for this, this is perfectly legal in a standard module:
Option Explicit
Public Property Get MyString() As String
MyString = "\R;;" & Chr(163)
End Property
If re-creating the string every time the property getter is accessed is a problem, then you need a way to persist its value in a backing field - but then, this backing field (whether it's in a class or standard module) only has a value when the execution context exists, meaning it has exactly the same problem a global variable would have: execution stops, it's gone.
One work-around could be to use a class with the VB_PredeclaredId attribute set to True (default is False).
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Option Explicit
Private internalString As String
Private Sub Class_Initialize()
internalString = "\R;;" & Chr(163)
End Sub
Public Property Get MyString() As String
MyString = internalString
End Property
And now VBA will automatically create an instance of Class1 whenever it's referenced, as soon as it's referenced, and that instance remains "alive" until an End statement is explicitly executed, or the execution context is otherwise terminated. Exactly like, say, a UserForm class, you access this default instance using the class name as an identifier:
Debug.Print Class1.MyString
If the default instance existed when Class1 was referenced, the internalString is returned. If it didn't, then Class_Initialize executes, then the internalString is returned.

VBA: Adding class items to a collection within class

I'm trying to make a tree (kind of a composite pattern actually), but I just can't add the created items of a class to a collection of items in the parent.
Inside the class
Private pChildList As Collection
Private Sub Class_Initialize()
Set pChildList = New Collection
End Sub
Public Property Set ChildList(Value As CProduct)
pChildList.Add Value
End Property
Public Property Get ChildList() As Collection
ChildList = pChildList
End Property
The main function calling
Set Pro = New CProduct
Set Child = New CProduct
Pro.ChildList.Add Child
So the result should be a parent (Pro) with a Child in its pChildList collection, but I only get the error that "Argument is not optional".
Many thanks in advance!
You are just missing a Set in your property Get definition. A Collection is an object, you need to use the Set keyword to affect it to a variable.
Public Property Get ChildList() As Collection
Set ChildList = pChildList
End Property
To complement my answer following you comment:
Property Set are for Objects, Property Let are for base types. Those two properties are usually used to change the value of a member variable (and are expected to do that), that is to access the variable for writing, but you can do whatever you want in the code.
Property Get are usually used to return the value of a member variable (but once again, you can do whatever you want in code), that is to access the variable for reading.
Since there is no reason to change the pChildList itself, I would drop totally the Property Set.
You can also decide to completely hide the member variable and use member functions to add and remove Childs, for example:
Public Sub AddChild(vValue as CProduct)
pChildList.Add vValue
End Sub

VB.NET Property as StringDictionary?

I'm trying to create a new property with a type of StringDictionary but when I use the property like a stringdictionary variable I am getting an error.
The goal is to NOT use a variable, I need to use a property for this. Behind the scenes I am saving the stringdictionary value to a global user-ID indexed collection. Here's the code that creates the property and attempts to get and set:
Public Property MaxLenDict As StringDictionary()
Get
Return GetFromCollection("MaxLenDict")
End Get
Set(Value As StringDictionary())
SaveToCollection("MaxLenDict", Value)
End Set
End Property
Public Sub ExampleSub()
If MaxLenDict("hello world") = "" Then MaxLenDict.Add("Hello World", "I'm Here")
End Sub
Get this error in ExampleSub "StringDictionary cannot be converted to string" in the IF statement on this code:
MaxLenDict("hello world")=""
So how do I successfully make and use a property as a stringdictionary?
Your property is of type StringDictionary() (an array!), not StringDictionary.
I’m not sure that using StringDictionary is advised in the first place, though. The class is simply a remnant from pre-generics versions of .NET. Use Dictionary(Of String, String) instead.

How to set the text of list items in Windows Forms

I am trying to figure out how to set the value of the text that is displayed for each item of a list control, such as a checkbox list, but really this applies to most list controls, not just the checklistbox control.
I have a checklistbox control,
Friend WithEvents clstTasks As System.Windows.Forms.CheckedListBox
that I usually want to populate with a list of task names. I call the add method to add a Task object to the list. I know that if I override the ToString method, whatever value is returned by that function will be displayed as the text for the list item.
However, in rare situations, I want to display something else other than just the name. For example, perhaps I want to display the name and the value of another property, such as the value of the Boolean property "Optional" shown in parenthesis following the name.
What is the best way to do this?
The best that I can think of is to define a property which is set in the GUI layer and then used by the ToString function to determine how it should behave when called. If the controlling property is set to one value, the ToString will return the Name, else it will return the Name followed by the value of the Optional flag. This seems a little disjoint a kludged to me.
The other alternative which seems a little overkill is to define a new class which inherits from Task, for example TaskOptional, which overrides the Tostring method on the base class. In this subclass, the ToString function would return the Name/Optional flag value. However, this, too, seems a little nuts to have to come up with a new class just to modify how the text is being displayed in the presentation layer. I feel that I should be able to control this in the presentation layer without changing the business object or creating a new derived object.
What is the best way to accomplish this?
For Each CurrentTask As Task In _MasterTaskList
clstTasks.Items.Add(CurrentTask, True)
Next
Public Class Task
Private _Name As String
Private _Optional As Boolean
Public Sub New (name As String, optional As Boolean)
_Name = name
End Sub
Public Overrides Function ToString() As String
Return _Name
End If
End Function
End Class
You can set the DisplayMember property of your CheckedListBox to the name of one of your custom class' property.
Let's say you create a property like the following:
Public ReadOnly Property NameOptional() As String
Return _Name & " (" & _Optional & ")"
End Property
then you can set the display member like this:
clstTasks.DisplayMember = "NameOptional"
When you set a display member, this property will be displayed instead of the ToString value.
You could do the following
Public Overrides Function ToString() As String
Return string.Format("{0}{1}", _Name, IIF(_Optional, " (Optional)", ""))
End Function
EDIT: You will have to set the value of _optional in the constructor, which is missing in the code you have provided.