I've been working on this project for two weeks now and I'm drained from all the hiccups I've come across. Now I've hit a brick wall.
I'm making a "simple" order form in Visual Basic 2010 that calculates the amount due for an order and then from the total determines what the shipping will be. I've been able to accomplish the following tasks:
accept customer data (name, address, city, state abbreviation, zipcode)
calculate total with shipping costs (accurately, for the most part)
clear the order form and exit
But the way I have it right now, a customer can only put in 1 order form.
Problem: I have to figure out a way to allow a customer to place an order form for multiple items. (I'll assume that a customer will not place an order for more than 20 items). So what would be the best way to go about saving the information for multiple items then calculating the cost at the end?
If it's multiple form creation, how is that done? Is the keyword "Container" the way to go, and how? At the end of the day, I just don't know how to create this multiple item form (it's like a database in Visual Basic), so any method, at least pointing me in the right direction, would be helpful.
(Tried to post an image of my work so far but I'm new here - can't post images yet. I've posted a bit of my code below - code works for 1 order)
Snippet Code:
Private Sub total_mouseleave(ByVal sender As Object, ByVal e As EventArgs) Handles total.MouseLeave
Dim gs_1 As Decimal = 4.95
Dim gs_2 As Decimal = 6.95
Dim gs_3 As Decimal = 8.95
Dim gs_4 As Decimal = 10.95
Dim gs_5 As Decimal = 12.95
Dim ex_1 As Decimal = 14.95
Dim ex_2 As Decimal = 16.95
Dim ex_3 As Decimal = 18.95
Dim ex_4 As Decimal = 20.95
Dim ex_5 As Decimal = 22.95
Dim totalcost As Decimal
totalcost = Convert.ToDecimal(total.Text)
If gs_radiobutton.Checked Then
If (totalcost < 1) Then
total.Text = Val(0)
shipping.Text = Val(0)
ElseIf (1 < totalcost AndAlso totalcost < 15) Then
total.Text = Val(totalcost + gs_1)
shipping.Text = ground_1
ElseIf (15 < totalcost AndAlso totalcost <= 49.99) Then
total.Text = totalcost + gs_2
shipping.Text = gs_2
ElseIf (50 < totalcost AndAlso totalcost <= 99.99) Then
total.Text = totalcost + g_3
shipping.Text = gs_3
ElseIf (100 < totalcost AndAlso totalcost <= 199.99) Then
total.Text = totalcost + gs_4
shipping.Text = gs_4
Else
total.Text = totalcost + gs_5
shipping.Text = gs_5
End If
End If
'// . . . .
End Sub
It's not clear from your post if you mean order forms as in a Form object within VB or an order placed by the user.
If you meant orders placed by the user then it would be simpler to use a single form but create a few custom classes to hold all the order line details:
' Class used for each order line
Public Class orderline
Public Value As Decimal
Public StockID As Integer
Public Qty As Integer
End Class
' Class used to store the entire order
Public Class Order
' List of orderlines (similar to an array)
Public OrderLines As List(Of orderline)
Private _totalcost As Decimal
Public ReadOnly Property TotalCost As Decimal
Get
Dim t As Decimal
For Each l In OrderLines
t = t + (l.Value * l.Qty)
Next
Return t
End Get
End Property
End Class
Then in the form you can use the classes as below:
Public Class Form1
Dim ord1 As New Order
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
TextBox1.Text = ord1.TotalCost
End Sub
End Class
You can then create more than one order stored on the form using a list:
Dim CustomerOrders as List<Of Order>
You should be able to create multiple instances of a form and show them non-morally. Whether this is the best design is another discussion.
The non-modal forms would all be visible and can be 'owned' by a main form.
Try something like this:
Dim firstInstance As New YourForm()
Dim secondInstance As New YourForm()
firstInstance.Show() 'Not ShowDialog()
secondInstance.Show(myParent) 'Optional parent form parameter
You'll need to watch the scope of your form instances as the forms will be closed if the variables go out of scope.
This may not be the ultimate solution, but it's something you can try on the way to finding the optimal approach.
Considering the latest comment:
The problem is not clear. Do you want to create multiple forms, one form per order? Or do you want one form that can show multiple orders?
My solution will allow you to show multiple forms while the other answer posted suggests a neat solution to creating one form with multiple orders.
Personally, I would create a single form that shows multiple orders, ListView maybe, that provides some way of viewing/editing each order's details and then allowing the orders to be submitted in a batch. It's more complex but would be more usable, IMHO.
Related
Pretty much just the title, I've been learning vb for my computer science A-level and ran into some trouble with this exercise. I made a procedure to affect the final cost of an invoice by comparing the current date to the due date, but the due date that I input does not seem to have any effect on the final cost.
Form:
Invoice Form
Task:
Write a program that processes invoices for a company selling a variety of products. Ask the user to enter the unit cost of the product, how many were sold and the date the invoice had to be paid by. A check box should be used to indicate if the product is VAT rated. When these details have been entered the user should click a button. This event should call two general procedures. The first should calculate and return the basic cost of the invoice, including VAT. The second should reduce the basic cost by 10% if the invoice has been paid on time. The final cost should be displayed by the Click event of the button.
Code:
Public Class Form1
Dim invoice As Integer
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
Dim unitCost As Integer = txtCost.Text 'Input cost of product in textbox
Dim unitsSold As Integer = txtUnits.Text 'Input units sold in textbox
Dim dueDate As Date = dtpDueDate.Value 'Input date in date and time picker
Dim VATCheck As Boolean = chkVAT.Checked 'Input VAT rating in checkbox
Call InvoiceProcess(unitCost, unitsSold, VATCheck)
Call DueCheck(dueDate, invoice)
MsgBox(invoice)
End Sub
Sub InvoiceProcess(ByRef price As Integer, ByRef units As Integer, ByRef VAT As Boolean)
If VAT = True Then
invoice = 1.2 * price * units
Else
invoice = price * units
End If
End Sub
Sub DueCheck(ByRef dateDue As Date, ByVal invoice As Integer)
Dim todayDate As Date = Today.Date 'Current date
Dim overDue As Integer = DateTime.Compare(todayDate, dateDue.Date)
If overDue <= 0 Then
invoice = invoice * 0.9
End If
End Sub
End Class
The question says "This event should call two general procedures. The first should calculate and return..." - notice it says "return" - that means that it needs to be a function, not a sub.
Once you've got that fixed, the invoice value can be passed from one method to another in the parameters, so you can remove the Dim invoice As Integer from where it is because it is currently scoped to entire class, which you probably don't want.
Also, invoice should be a Decimal, not an Integer.
I am trying to write a program that will allow a user to generate a random list of names. I have a gridview of names from a SQL Db when the form launches. Is it possible to generate a random list from the names in the gridview or does that have to come from another Sql Connection string and reference different parameters? I was trying to display random names from the gridview to a listbox. Thank you.
Here is the code that I have been trying to experiment with:
Private Sub btnDraw_Click(sender As Object, e As EventArgs) Handles btnDraw.Click
Dim listCount As Integer
Dim i As Integer = 0
Dim rnd As New Random
Dim listselection As Integer
listCount = grdEmployees.
Do While i < CInt(grdEmployees.Text)
'randomize selection
listselection = rnd.Next(0, grdEmployees.Items.Count)
lstSelected.Items.Add(grdEmployees.Items(listselection))
grdEmployees.Items.RemoveAt(listselection)
'increment i
i += 1
Loop
txtQuantity.Text = String.Empty 'Clears box after entry
End Sub
You could do it through your SQL query:
SELECT TOP 25 SomeField FROM SomeTable ORDER BY RAND()
Or through your managed code. Which is best depends on the size of the table and where you want the sorting to be done. If you prefer to sort on the server, or locally.
I am creating a menu application, I have at the moment 3 textboxes, textbox1 is price, textbox2 is quantity and textbox3 is total. I have successfully written the code to calculate the price of an item depending on the quantity they need. The code i have right now:
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
TextBox8.Text = CStr(Val(TextBox6.Text) * Val(TextBox7.Text))
End Sub
Now what I need is that I will have more items say 10 along with textboxes beside as quantity which makes 20 textboxes and one total textbox. How can I write the code so that it calculates every textbox as well as null values say if out of the 10 items i want only 2 items 1 per quantity into the total value.
Thanks
First, i strongly suggest to use .NET methods instead of old VB methods. I also would set OPTION STRICT to On in general which avoids "magical" conversions done for you by the runtime. Instead you have to specify the correct types which is a good thing since it can prevent errors on runtime and it also helps to learn the .NET types and methods.
I would add those TextBoxes all to the same container-control (like a Panel or something similar). I also suggest to use more meaningful names for your controls(f.e. TxtTotal for the total-textbox).
So if all price-textboxes' names start with TxtPrice (f.e. TxtPrice1 etc) and all quantity-TextBoxes start with TxtQuantity (f.e. TxtQuantity1 etc), this LINQ query approach will work:
Dim allTextBoxes = PriceQuantityPanel.Controls.OfType(Of TextBox)()
Dim allPrices = From txt In allTextBoxes
Where txt.Name.StartsWith("TxtPrice")
Dim allQuantities = From txt In allTextBoxes
Where txt.Name.StartsWith("TxtQuantity")
Dim price As Decimal
Dim invalidPrices = From txt In allPrices
Where Not Decimal.TryParse(txt.Text, price)
If invalidPrices.Any() Then
MessageBox.Show("Please enter valid prices(Decimal)!")
Return
End If
Dim quantity As Int32
Dim invalidQuantities = From txt In allQuantities
Where Not Int32.TryParse(txt.Text, quantity)
If invalidQuantities.Any() Then
MessageBox.Show("Please enter valid quantities(Integer)!")
Return
End If
Now you can "join" the pairs of price and quantity textboxes by the number-suffix:
Dim query = From txtP In allPrices
Join txtQ In allQuantities
On txtP.Name.Substring("TxtPrice".Length) Equals txtQ.Name.Substring("TxtQuantity".Length)
Select New With {.Price = Decimal.Parse(txtP.Text), .Quantity = Int32.Parse(txtQ.Text)}
Dim totalSum As Decimal = query.Sum(Function(x) x.Price * x.Quantity)
TxtTotal.Text = totalSum.ToString()
I have a program below that doesn't seem to be doing what I want it to do. In general, the pseudocode is: enter the number of miles (miles.text), click button, check: is the mileage entered equal to or less than the mileage radius (milestotextbox) in the database? If so, grab the truckload rate that corresponds to that radius (truckloadratetext) and display it in a textbox called "rate" (rate.text) and if not, continue looking until EOF. I've shown the code below. It lets me enter the mileage but won't check and display the result.
The data in the table looks like this:
ID MILESTO TRUCKLOADRATE
1 50 200
2 100 300
3 200 700
4 300 800
So if someone enters a mileage like 10, I want it to take the truckload rate of $200. If someone enters 250, the rate would then be 800. I'm not too hung up right now about what happens if a mileage is out of range. Just trying to figure out why the mechanics of something like this isn't working. It's my first time using records with a LOOP command so I'm trying to keep it straightforward with my program.
What could I be doing wrong? Thank you in advance and hope all has a great New Years!
Public Class Form1
Private Property EOF As Boolean
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the '_test_2DataSet.test' table. You can move, or remove it, as needed.
Me.TestTableAdapter.Fill(Me._test_2DataSet.test)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Do Until EOF()
If Val(MilestoTextBox.Text) <= Val(Miles.Text) Then
rate.Text = TruckloadTextBox.Text
End If
Loop
End Sub
End Class
I neither know where you set the EOF variable nor do i understand its purpose. Have a look at following example which shows how to loop all rows of a DataTable(ORDER BY MILESTO ASC) to find the closest value greater than the given value:
Dim mileAge = Int32.Parse(Miles.Text)
Dim rate = 0
For Each row In _test_2DataSet.test
If mileAge <= row.MILESTO Then
rate = row.TRUCKLOADRATE
Exit For
End If
Next
If rate <> 0 Then
TxtRate.Text = rate.ToString
End If
If you cannot order by MILESTO initially or you simply want to see another approach that is not a database query, try this LINQ-To-DataSet approach:
rate = (From r In _test_2DataSet.test Order By r.MILESTO
Where mileAge <= r.MILESTO
Select r.TRUCKLOADRATE).FirstOrDefault
If you want to query the database, follwoing SQL returns the nearest TRUCKLOADRATE that is greater/equal than the #MileAge-parameter:
SELECT TOP (1) TRUCKLOADRATE
FROM Test
WHERE (MILESTO >= #MileAge)
ORDER BY MILESTO - #MileAge
Add a query to your DataAdapapter that returns a single value and has a meaningful name like getTruckloadRateByMileAge. Then it's that simple:
Dim loadRate = DirectCast(daTest.getTruckloadRateByMileAge(mileAge), Decimal)
Sorry for the delay in answering the question here and I want to thank everyone who answered, especially Tim.
In summary, Where a user wants to search through a dataset and compare a user-inputted value against some kind of index (in my case, if a mileage entered is less than or equal to some boundary value) and get the corresponding record that satisfies that criteria, the solution that works (thank you Tim!) is:
Dim mileAge = Int32.Parse(Miles.Text)
Dim rate = 0
For Each row In _test_2DataSet.test
If mileAge <= row.MILESTO Then
rate = row.TRUCKLOADRATE
Exit For
End If
Next
If rate <> 0 Then
TxtRate.Text = rate.ToString
End If
In the example, mileage is converted to a value that can be used to compare against a column in the dataset called MILESTO, which is a mile radius that corresponds to a price for transporting goods called TRUCKLOADRATE. The program iterates through each row in the dataset until the condition that mileage <= row.MILESTO is satisfied.
My original thought was using a search until EOF and this method works better.
Thanks all and again my apologies for the delay in summing this up for other users.
Trying to get the user to put 3 numbers in 3 text boxes and get the average.
Private Sub btnAverage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAverage.Click
Dim a As Integer = CInt(txtone.Text)
Dim b As Integer = CInt(txtTwo.Text)
Dim c As Integer = CInt(txtThree.Text)
Dim average As Integer
average = (a + b + c) / 3
lstOutput.Text = average
End Sub
Try changing the type of average from Integer to Double
Dim average as Double
Right now you're trying to store the Average in an Integer which can only hold a whole number. Averages tend to be non-whole numbers and need a data type that can represent that. Double is good for most situations. That should fix your problem.
EDIT OP mentioned that lstOutput is a ListBox
This is one of the confusing things with WinForms. Even though every single control has a Text property, not all of them actually do anything. They only apply to elements that directly display a single text block or value. Ex Button, Label, etc ...
A ListBox on the other hand displays a group of items. You want to add a new item to the list.
lstOutput.Items.Add(average.ToString())
The Text property of a list box will get or set the selected item. You haven't added your average to the listbox yet.
Try:
lstOutput.Items.Add(average)
Are you sure that txtOne.text txtTwo.text and txtThree.txt will always be an integer value?
You might need to also change the a,b,c vars to Doubles and check that the user didn't supply non-numeric values.
If the user puts "one" in the txtOne textbox, you'll get an exception kablowee.
(air coding here)
dim a as new double
try
if isnumeric(txtOne.text.tostring.trim) then
a = cdbl(txtOne.text.tostring.trim)
end if
'repeat for b and c ...
catch ex as exception
messagebox.show(ex.message.tostring)
end try
And, I'm not sure if I'm right about this, (maybe someone will enlighten me) but does .NET consider type conversion from string to int differently in these two cases
a = cint(txtOne.text)
and
a = cint(txtOne.text.tostring)
???