How to accumulate the sum in VB? - vb.net

Dim index As Integer
Dim choice As String
Dim total As Integer
total = 0
index = NumericUpDown1.Value
Dim arr(4) As Integer
arr(0) = 10
arr(1) = 5
arr(2) = 21
arr(3) = 33
If index > 0 Then
choice = (Combobox1.SelectedItem.ToString + " x " + NumericUpDown1.Value.ToString)
ListBox1.Items.Add(choice)
CheckedListBox1.Items.Add(choice)
total += arr(Combobox1.SelectedIndex) * index
TotalLabel.Text = total.ToString()
Else
MsgBox("error.")
End If
I can calculate the total of single choice, but fail to accumulate to sum.
What's wrong of the code?
Current Situation:
Step 1:
choose arr(0), index = 2
total = 20
Step 2:
choose arr(2), index = 1
total = 21
Correct Situation:
Step 1:
choose arr(0), index = 2
total = 20
Step 2:
choose arr(2), index = 1
total = 41

You'll need a either a global variable or a class with a public variable. You should create a Transaction class to store the data about the transaction and probably a Product class to store the data about the product. What you put in it is up to you, but I'd start out with something like this:
Public Class Transaction
Private _productsList As List(of Product)
Private _transationNumber As Integer
'...more stuff...
'you'll want to remember what products are in your "cart" for the transaction
Public Property ProductsList As List(of Product)
'your get/set accessors
End Property
Public Property TransactionNumber As Integer
'your get/set accessors
End Property
Public Property TotalTransactionCost() As Double
Get
'this will sum of the prices of all of the products you have stored in your
'list of products for this transaction
Return _productsList.Sum(product => product.Price)
End Get
End Property
Public Sub New()
'...constructor stuff
End Sub
Public Sub AddProductToTransaction(byval product)
_productsList.Add(product)
End Sub
End Class
Public Class Product
Private _price As Double
Private _productName As String
Private _UPC As String
Public Property Price() As Double
'your get/set accessors
End Property
Public Property ProductName() As String
'your get/set accessors
End Property
Public UPC As String () As String
'your get/set accessors
End Property
Public Sub New()
'constructor stuff
End Sub
End Class
These are a couple class shells to get you started. If you're serious about making a product, this is a step in the right direction. If you're going to write code, write it the right way.
If you're just looking for a quick and dirty solution, you can declare a global variable and just keep a running sum. Just don't forget to clear it out before you start a new transaction.
You'll want to do something like:
Private TransactionCost As Double in your form outside of all your methods.
Again, I would recommend the first way of going about things. You'll need at least those two classes and they'll definitely be more fleshed out for a real product.
I hope this helps and answers your question. If it does, hit me with an upvote and accept the answer. Welcome to SO.

Related

Adding member to list overides previous members

I need to read from an Excel file a list of dogs with their birth date, and store them in a list. The read works well, and every time I add a new dog to the list, the list count grows. So far so good.
The problem is that when I add the 2nd record, the first record is replaced by the first record content, so I have two identical records. And so on with every new record. In the end, I have many same records - all with the value of the last dog.
The dog class is:
Public Class DogClass
Public Name As String
Public Dob As Date
Public Age As Integer
Public Sex As String
Public Sub setDogName(ByVal _name As String)
Name = _name
End Sub
Public Sub setDogDOB(ByVal _dob As Date)
Dob = _dob
End Sub
Public Sub setDogAge(ByVal _age As String)
Age = _age
End Sub
Public Sub setDogSex(ByVal _sex As String)
Sex = _sex
End Sub
End Class
The class of the list is:
Public Class DogsListClass
Public dogsList As New List(Of DogClass)
Public Function DodgsCnt() As Integer
DodgsCnt = dogsList.Count()
End Function
Public Function DogExsists(_dogName As String) As Boolean
Dim res As Boolean = False
For Each item As DogClass In dogsList
If item.Name = _dogName Then
res = True
End If
Next
Return res
End Function
Public Sub AddDog(_dog As DogClass)
dogsList.Add(_dog)
End Sub
End Class
The calling:
Dim tdog As New DogClass
Dim DogsList As New DogsListClass
Do
tdog.setDogName(MyExcel.Cells(row_cnt, col_cnt).text)
tdog.setDogDOB(MyExcel.Cells(row_cnt, col_cnt + 1).value)
DogsList.AddDog(tdog)
Loop
Any idea why the records are being overridden?
DogsList variable must be declared outside the Do...Loop, otherwise you are creating a "New" DogsList at each iteration and, based on this code, you should end up having one single item in the collection, not many.
Also, declaring DogsList in the loop, prevents you from using it outside in the rest of your code.

Naming global variables in a structured way

My current project has global constants that define certain rows and columns in workbooks that this project will be searching through. I have defined them as such:
Public Const headRow As Integer = 1
Public Const descRow As Integer = 2
Public Const pnumCol As Integer = 1
Public Const teamCol As Integer = 2
Public Const dateCol As Integer = 3
Public Const hourCol As Integer = 4
Public Const typeCol As Integer = 5
Public Const taskCol As Integer = 6
Public Const noteCol As Integer = 7
I'm wondering if there is a cleaner way to define these that would allow me to write these in a way such as:
ColumnNums.team
ColumnNums.task
ColumnNums.note 'etc
I think something similar to this could be done by defining my own type, but that would probably not be worthwhile. I'm basically wanting this to be an easy way to remember the variable names as I write more code, as well as to be able to count how many items I have in each group. Would a Type or Collection be useful in this case?
For mixed variable types, you can put it in a class module, name the class module ColumnNumbers and put the following code in:
Public Property Get Team() As Long
Team = 1
End Property
Public Property Get TeamName() As String
TeamName = "Team One! :-)"
End Property
Then you can use it in any module like this:
Dim colNums As New ColumnNumbers
Sub foo()
MsgBox colNums.Team
End Sub
If you only want to return long values, put it in an enum:
Enum ColumnNumbers
Team = 1
Description = 2
End Enum
Sub foo()
MsgBox ColumnNumbers.Team
End Sub
Chip pearson has already done a fantastic job of describing enums here it's worth a read if you have yet to discover them.
You could use public arrays like this:
Public ColumnNum(0 To 2) As Long
Public RowNum(0 To 2) As Long
Used together with an enum:
Public Enum Category
team
task
note 'etc.
End Enum
Then things like ColumnNum(team) will function like a public variable:
Sub test1()
ColumnNum(team) = 5
End Sub
Sub test2()
Debug.Print ColumnNum(team)
End Sub
If these two subs are run in order than 5 is printed.

Can I create variables dynamically based on other variables?

I'm working on a VBA module to process lists of quote items. My current boggle is trying to stack full or partial sets of things from the quote lists, and I'm trying to figure out how to keep track of them.
The item lists do not have a consistent number of items; one might be a single item, another might be a hundred.
The system divides the cargo into four broad types (Pipes, Plates, Beams and Other) for the sake of selecting which calculator logic to use.
Is there any way to create variables on the fly to keep track of individual line items? For instance, deploying a spot of pseudocode:
Public Qty & "_" & Class & "-" & ClassCount As Integer
Is there any way to make something like that work, or is there a better way to do it?
I'm a bit sketchy on classes, and I really should start looking at them more as they're very powerful - this link will give you more info: http://www.cpearson.com/excel/classes.aspx
Expanding on Jasons comments this is one way of building the class, and I'm sure there's a much better way of doing it:
Add a Class Module to your project and name the module cls_Quote.
Add this code to the class module:
Option Explicit
Private sQuote As String
Private lQuantity As Long
Private lAnotherValue As Long
Public Property Let Quote(Value As String)
sQuote = Value
End Property
Public Property Get Quote() As String
Quote = sQuote
End Property
Public Property Let Quantity(Value As Long)
lQuantity = Value
End Property
Public Property Get Quantity() As Long
Quantity = lQuantity
End Property
Public Property Let AnotherValue(Value As Long)
lAnotherValue = Value
End Property
In a normal module add this code:
Option Explicit
Private MyQuotes As Collection
Public Sub Test()
Dim MyNewQuote As cls_Quote
Dim x As Long
Dim vIQuote As Variant
Dim FinalSum As Long
Set MyQuotes = New Collection
For x = 1 To 10
Set MyNewQuote = New cls_Quote
With MyNewQuote
.Quantity = x
.Quote = "Pipes"
.AnotherValue = x * 5
End With
MyQuotes.Add MyNewQuote
Next x
For Each vIQuote In MyQuotes
If vIQuote.Quote = "Pipes" Then
FinalSum = FinalSum + vIQuote.Quantity
End If
Next vIQuote
MsgBox "Total sum of Pipes is: " & FinalSum
End Sub
Note: In the For x loop I'm creating a new instance of the class each time.
Now just waiting for someone with more class programming experience to show a much better way of doing it. :)

Vb conceptual understanding of creating objects within a class

I was wondering if you could help me understand how to create a Card object (with a value and suit) and use 52 of those cards to make an object called deck.
I have created my card class how do I initialize every card inside the deck class? Should I do it one by one? How do I link all those cards to one deck.
Thanks
As it happen I did read your previous question earlier today.
First, create a suit enum.
Public Enum Suit As Integer
Hearts = 1
Diamonds = 2
Clovers = 3
Spades = 4
End Enum
Then create the card class. Notice that the properties are read only as a card never changes its value. (Maybe not true if you're a magician)
Public Class Card
Public Sub New(suit As Suit, value As Integer)
Me.m_suit = suit
Me.m_value = value
End Sub
Public ReadOnly Property Suit() As Suit
Get
Return Me.m_suit
End Get
End Property
Public ReadOnly Property Value() As Integer
Get
Return Me.m_value
End Get
End Property
Private m_suit As Suit
Private m_value As Integer
End Class
Finally, create the deck class and populate 52 cards.
Public Class Deck
Public Sub New()
Dim cards = New Card(52 - 1) {}
Dim num As Integer = 0
For s As Integer = 1 To 4
For v As Integer = 1 To 13
cards(num) = New Card(CType(s, Suit), v)
num += 1
Next
Next
Me.m_cards = New Collections.ObjectModel.ReadOnlyCollection(Of Card)(cards)
End Sub
Public ReadOnly Property Cards() As Collections.ObjectModel.ReadOnlyCollection(Of Card)
Get
Return Me.m_cards
End Get
End Property
Private ReadOnly m_cards As Collections.ObjectModel.ReadOnlyCollection(Of Card)
End Class
You need two Enumerations and two Classes:
Enumerations
CardFaceValue - with values ranging from Ace-10 (inclusive), Jack, Queen, King.
CardFaceType - with values Hearts, Spades, Clubs, Diamonds
Classes
Deck - Has one property to contain the collection of all cards
Cards - of Type Array of Cards, sized 52.
Card - Has two properties
CardFaceValue
CardFaceType
In the constructor of the Deck class run a loop within a loop. The outer loop will run for 4 times for each of the CardFaceType enumeration, and the inner loop will run for 13 times for cards 1-10, J, Q, K.
With these loops iterate through the enumeration values and add cards to your Deck.
This is just a quick draft of what I envision
You'll need the card class first.
Public Class Card
Private cSuit As String
Private cValue As Integer
Public Property suit() As String
Get
Return cSuit
End Get
Set(ByVal value As String)
cSuit = value
End Set
End Property
Public Property value() As Integer
Get
Return cValue
End Get
Set(ByVal value As Integer)
value = cValue
End Set
End Property
Public Sub New(ByVal TheSuit As String, ByVal TheValue As Integer)
cSuit = TheSuit
cValue = TheValue
End Sub
Then you can make a new object for each card and add it to the deck collection.
Dim Deck As New List(Of Card)
Dim Suit As String = "Spade"
Dim Value As Integer = 11
Dim AceOfSpades As New Card(Suit, Value)
Deck.Add(AceOfSpades)

VB.NET For Each Loop exits after one iteration

I have a For Each loop that is only iterating 1 element. It begins with the 6th element out of 32 in my test, and happens to be one where the comp.includeMe evaluates to True. After the outer if statement is executed, it begins the 2nd iteration but exits the loop and returns immediately after the comp.includeMe evaluated to false. No errors or warnings are present, and I have verified that there are elements in the components object. Can anyone explain what I am doing wrong, and why this syntax doesn't work?
Public Class BOM
Public Property components as New List(Of Component)
Public Function TotalArea(ByVal adjusted As Boolean) As Double
Dim total As Double = 0
For Each comp As Component In components
If comp.includeMe = True Then
If adjusted Then
total += comp.GetAdjustedSize() * comp.quantity
Else
total += comp.area * comp.quantity
End If
End If
Next
Return total
End Function
public sub Add(byval comp as Component)
components.add(comp)
end sub
End Class
Public Class Component
Public Property quantity as Integer
Public Property area as Double
Public Property includeMe as Boolean
...
End Class
' object construction
Dim bomlist as New BOM
bomlist.add(comp)
After digging a little deeper, it seems that the foreach statement is recognizing the first if statement and pulls values only if it is true. I realized I only had only had one component with the includeMe Boolean set to true. After I set other components to true as well, I observed that the For Each iterates exactly the number of times as the number of components with includeMe = True
I'd suggest adding some debug statements to assist with debugging:
Public Class BOM
Public Property components as New List(Of Component)
Public Function TotalArea(ByVal adjusted As Boolean) As Double
Dim total As Double = 0
Debug.Print(components.Count)
For Each comp As Component In components
Debug.Print(comp.includeMe)
If comp.includeMe = True Then
If adjusted Then
total += comp.GetAdjustedSize() * comp.quantity
Else
total += comp.area * comp.quantity
End If
End If
Next
Return total
End Function
public sub Add(byval comp as Component)
components.add(comp)
end sub
End Class