I am in a basic C# programming class, and I have been understanding everything up until I had to do the following exercise. If someone could check my code and help me out, that would be fantastic. I'm getting three different errors (CS1620, CS0266, and CS1955) in this code. I had a classmate try to help me out, but that did not work out so well.
Here is the exercise prompt:
Piecework workers are paid by the piece. Workers who produce a greater quantity of output are often paid at a higher rate.
Form: Use text boxes to obtain the person’s name and the number of pieces completed. Include a Calculate command button to display the dollar amount earned. You will need a Summary button to display the total number of pieces, the total pay, and the average pay per person. A clear button should clear the name and the number of pieces for the current employee.
Include validation to check for missing data. If the user clicks on the Calculate button without first entering a name and number of pieces, display a message box. Also, you need to make sure to not display a summary before any data are entered; you cannot calculate an average when no items have been calculated. You can check the number of employees in the Summary event procedure or disable the Summary command button until the first order has been calculated.
Pieces completed Price paid per piece for all pieces
1-199 .50
200-399 .55
400-599 .60
600 or more .65
Code:
public partial class pieceworkForm : Form
{
//Declare variables.
int tpiece = 0;
int numemp = 0;
float tpay = 0;
public pieceworkForm()
{
InitializeComponent();
}
private void exitButton_Click(object sender, EventArgs e)
{
//Closes form.
this.Close();
}
private void printButton_Click(object sender, EventArgs e)
{
//Prints form.
printForm1.PrintAction = System.Drawing.Printing.PrintAction.PrintToPreview;
printForm1.Print();
}
private void calcButton_Click(object sender, EventArgs e)
{
//Converts pieces to integer.
int pieces = 0;
pieces = int.Parse(piecesTextBox.Text.Trim());
//Calculates pay based on number of pieces completed.
float pay = calcButton(pieces);
//Display the formatted text in the pay label
payMaskedTextBox.Text = string.Format("C", pay);
payMaskedTextBox.Visible = true;
//Counts employees on click.
numemp = numemp + 1;
//Total pieces made.
tpiece = tpiece + pieces;
//Total pay.
tpay += pay;
//Enable summary button.
sumButton.Enabled = true;
}
private void clearButton_Click(object sender, EventArgs e)
{
//Clears form.
empTextBox.Text = string.Empty;
piecesTextBox.Text = string.Empty;
payMaskedTextBox.Text = string.Empty;
}
private void callButton_Click(object sender, EventArgs e)
{
//Confirm clear.
if ((MessageBox.Show("Do you want to clear this form?", "Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes))
{
// Call the regular "Clear" button handler, as if it had been clicked as well.
clearButton_Click(sender, e);
//Reset everything to zero.
tpiece = 0;
tpay = 0;
numemp = 0;
// Make summary groupbox invisible.
summaryGroupBox.Visible = false;
// And disable summary button until new pay is entered.
sumButton.Enabled = false;
}
}
private void empTextBox_TextChanged(object sender, EventArgs e)
{
//Show message if field is empty.
if (string.IsNullOrWhiteSpace(empTextBox.Text))
{
MessageBox.Show("Please enter an employee name.");
}
}
private void piecesTextBox_TextChanged(object sender, EventArgs e)
{
//Show messgae if field is empty.
if (string.IsNullOrWhiteSpace(piecesTextBox.Text))
{
MessageBox.Show("Please enter the number of pieces completed.");
}
}
private float calc(int pieces)
{
float pay = 0;
switch (pieces)
{
case 0:
pay = 0;
break;
case 1: // 1 to 199
pay = pieces * 0.5;
break;
case 2: // 200 to 399
pay = pieces * 0.55;
break;
case 3: // 400 to 599
pay = pieces * 0.6;
break;
default:
pay = pieces * 0.65;
break;
}
return pay;
}
private void SetcalcButtonState()
{
// Assume false
calcButton.Enabled = false;
// Check for non-empty text
if (((empTextBox.Text.Trim().Length > 0) & (piecesTextBox.Text.Trim().Length > 0)))
{
int pieces = 0;
// TryParse will return true if the text is good as a number
if ((int.TryParse(piecesTextBox.Text.Trim(), pieces)))
{
calcButton.Enabled = true;
}
}
}
private void sumButton_Click(System.Object sender, System.EventArgs e)
{
//Show total pieces nd pay.
tpieceMaskedTextBox.Text = string.Format("{0}", tpiece);
tpayMaskedTextBox.Text = string.Format("C", tpay);
//Calculate and show average pay per employee.
avgMaskedTextBox.Text = string.Format("C", tpiece / numemp);
// Make the whole summary box visible
summaryGroupBox.Visible = true;
}
}
The specific problem areas are as follows:
CS1955 Non-invocable member 'pieceworkForm.calcButton' cannot be used like a method.
float pay = calcButton(pieces);
CS0266 Cannot implicitly convert type 'double' to 'float'. An explicit conversion exists (are you missing a cast?)
private float calc(int pieces)
{
float pay = 0;
switch (pieces)
{
case 0:
pay = 0;
break;
case 1: // 1 to 199
pay = pieces * 0.5;
break;
case 2: // 200 to 399
pay = pieces * 0.55;
break;
case 3: // 400 to 599
pay = pieces * 0.6;
break;
default:
pay = pieces * 0.65;
break;
}
return pay;
}
CS1620 Argument 2 must be passed with the 'out' keyword.
if ((int.TryParse(piecesTextBox.Text.Trim(), pieces)))
{
calcButton.Enabled = true;
}
CS1955 Non-invocable member 'pieceworkForm.calcButton' cannot be used
like a method.
Instead of calling a method you are calling an event handler. What you want to do is the following:
float pay = calc(pieces);
CS0266 Cannot implicitly convert type 'double' to 'float'. An explicit
conversion exists (are you missing a cast?)
You need to specify your real numbers as a float in order to save them into a float variable, as they are a double by default.
private float calc(int pieces)
{
float pay = 0f;
switch (pieces)
{
case 0:
pay = 0f;
break;
case 1: // 1 to 199
pay = pieces * 0.5f;
break;
case 2: // 200 to 399
pay = pieces * 0.55f;
break;
case 3: // 400 to 599
pay = pieces * 0.6f;
break;
default:
pay = pieces * 0.65f;
break;
}
return pay;
}
CS1620 Argument 2 must be passed with the 'out' keyword.
Passing any values by reference must be explicitly defined using the out keyword. You can read about it on MSDN.
if ((int.TryParse(piecesTextBox.Text.Trim(), out pieces)))
{
calcButton.Enabled = true;
}
Related
So in this class, I'm trying to use a switch statement to determine which radio button is checked in the form.
This is the part of the code that I'm focused on
private void lstTransactions_SelectedIndexChanged(object sender, EventArgs e)
{
int index = lstTransactions.SelectedIndex;
if (index != -1)
{
txtAmount.Text = entries[index, 0];
txtDate.Text = entries[index, 1];
chkCleared.Checked = bool.Parse(entries[index, 3]);
// entries[index, 2] is transaction type
//make switch comparison work correctly
// to determine which radio button should be checked
switch(entries[index, 2])
{
case TransactionTypes.Deposit.ToString():
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal.ToString():
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
}
}
Typing case TransactionTypes.Deposit.ToString(): and case TransactionTypes.Withdrawal.ToString(): gives me an error that says "CS0150 A constant value is expected". I tried looking up the error and I still can't figure out how to make the switch comparison work.
Here's the full Forms Code for Reference
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Transaction3
{
public partial class CheckbookForm : Form
{
public CheckbookForm()
{
InitializeComponent();
}
enum TransactionTypes { Deposit, ServiceFee, Withdrawal }
TransactionTypes transactionType;
private string[,] entries = new string[20, 4];
decimal balance = 0m;
decimal bankBalance = 0m;
private void SetError(Control whichControl, string message)
{
errorProvider1.SetError(whichControl, message);
}
private decimal IsValid()
{
bool flag=true;
decimal amount = 0m;
DateTime date;
if (DateTime.TryParse(txtDate.Text,out date))
{
if (date>DateTime.Today)
{
flag=false;
SetError(txtDate,"Date must be on or before today");
}
}
else
{
flag=false;
SetError(txtDate,"Date must be entered");
}
if (decimal.TryParse(txtAmount.Text, out amount))
{
if (amount<=0)
{
flag=false;
SetError(txtAmount,"Amount must be more than zero");
}
}
else
{
flag = false;
SetError(txtAmount,"Amount must be a number more than zero");
}
if (flag)
{
if (transactionType == TransactionTypes.Withdrawal)
{
if (balance >= amount)
{
amount *= -1;
}
else
{
flag = false;
SetError(txtAmount, "Insufficient funds");
}
}
else if (transactionType == TransactionTypes.ServiceFee)
amount *= -1;
}
if (!flag)
amount = 0;
return amount;
}
private void ShowBalance()
{
lblBalance.Text = balance.ToString("c");
lblBankBalance.Text = bankBalance.ToString("c");
}
private void ClearForm()
{
txtAmount.Clear();
txtAmount.Focus();
txtDate.Clear();
rbWithdrawal.Checked = true;
}
private void CheckbookForm_Load(object sender, EventArgs e)
{
rbDeposit.Tag = TransactionTypes.Deposit;
rbWithdrawal.Tag = TransactionTypes.Withdrawal;
rbServiceFee.Tag = TransactionTypes.ServiceFee;
ClearForm();
ShowBalance();
}
private void btnProcess_Click(object sender, EventArgs e)
{
errorProvider1.Clear();
decimal amount = IsValid();
if (amount != 0)
{
string entry;
string process = "Not Processed";
balance += amount;
if (chkCleared.Checked)
{
bankBalance += amount;
process = "Processed";
}
ShowBalance();
int row = lstTransactions.Items.Count;
entries[row, 0] = amount.ToString();
entries[row, 1] = txtDate.Text;
entries[row, 2] = transactionType.ToString();
entries[row, 3] = chkCleared.Checked.ToString();
entry = string.Format("{0} {1}: {2} ({3})", transactionType.ToString(),
txtDate.Text, amount.ToString("c"), process);
lstTransactions.Items.Add(entry);
}
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearForm();
}
private void btnReset_Click(object sender, EventArgs e)
{
errorProvider1.Clear();
DialogResult button;
button = MessageBox.Show("Clear all prior entries and set balance to $0?\nThis CANNOT be undone.",
"Reset Account", MessageBoxButtons.YesNo);
if (button == System.Windows.Forms.DialogResult.Yes) {
balance = 0;
bankBalance = 0;
ClearForm();
ShowBalance();
lstTransactions.Items.Clear();
}
}
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
private void rb_CheckedChanged(object sender, EventArgs e)
{
RadioButton rb = (RadioButton)sender;
if (rb.Checked)
transactionType = (TransactionTypes) rb.Tag;
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
btnExit_Click(sender, e);
}
private void CheckbookForm_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult button;
button = MessageBox.Show("Close and exit? All entries will be lost.",
"Exit?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2);
if (button == System.Windows.Forms.DialogResult.Cancel)
e.Cancel = true;
}
private void lstTransactions_SelectedIndexChanged(object sender, EventArgs e)
{
int index = lstTransactions.SelectedIndex;
if (index != -1)
{
txtAmount.Text = entries[index, 0];
txtDate.Text = entries[index, 1];
chkCleared.Checked = bool.Parse(entries[index, 3]);
// entries[index, 2] is transaction type
//make switch comparison work correctly
// to determine which radio button should be checked
switch(entries[index, 2])
{
case TransactionTypes.Deposit.ToString():
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal.ToString():
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
}
}
}
}
You should ditch the multidimensional array and go with a typed List or Array.
Multidimensional arrays are useful when the data is of the same type, yours it a bit of everything. so we in turn would make a class.
public class Entry
{
public Entry(decimal amount, DateTime date, TransactionType transactionType, bool cleared)
{
Amount = amount;
Date = date;
TransactionType = transactionType;
Cleared = cleared;
}
public decimal Amount { get; set; }
public DateTime Date { get; set; }
public TransactionType TransactionType { get; set; }
public bool Cleared { get; set; }
}
Given
// now an array of Entry
private Entry[] entries = new Entry[20];
Usage
To update update an entry
// add / update and array slot
entries[row] = new Entry(amount, DateTime.Parse(txtDate),transactionType,chkCleared.Checked);
Your switch would look like this
switch (entries[index].TransactionType)
{
case TransactionTypes.Deposit:
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal:
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
Note : This is not meant to be a working example, or to make your code work, it was merely showing you how you could used a typed array to make your life easier and show a potential for the switch without having to parse your string back into an enum
Alternatively you could just use Enum.Parse
Converts the string representation of the name or numeric value of one
or more enumerated constants to an equivalent enumerated object.
var tranType = (TransactionTypes)Enum.Parse(typeof(TransactionTypes), entries[index, 2]);
switch(tranType)
The error you are facing, has nothing to do with multidimensional arrays. It is due to using a runtime value inside a case logic.
You cannot use case TransactionTypes.Withdrawal.ToString() as a case statement. The last part ToString() is a function call. Even though it will always return the same value, the compiler does not know that. It will always think that the value of case TransactionTypes.Withdrawal.ToString() is not known at compile time, and thus is a potential risk for runtime error and thus will not compile.
You might wanna think what is the harm in having a runtime value. Consider the following code -
switch (entries[index].TransactionType)
{
case TransactionTypes.Withdrawal.ToString()://only known at wexecution not before that
throw a;
break;
case TransactionTypes.Withdrawal.ToString()://only known at wexecution not before that
throw b;
break;
default:
break;
}
Both the cases have same values. But the compiler cannot determine them unless it executes the code and when it will try to do that, there will be a clash in the decision. To save the code for such failure, having dynamic value in a switch-case is prohibited and that is the reason for that exception.
In future the compilers might be more intelligent to determine proper branching based on codes, but for now we are stuck with constant values for cases. So remove the function call or use Enum.Parse to get the enum values and do your check. Something like this -
if(Enum.TryParse<TransactionTypes>(entries[index, 2], true, out TransactionTypes parsed){
switch (parsed)
{
case TransactionTypes.Deposit://constant value, no issue
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal://constant value, no issue
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
}
else{
throw new Exception("Unknonw enum string");
}
get more details about Enum.Parse or Enum.TryParse here https://learn.microsoft.com/en-us/dotnet/api/system.enum.tryparse?view=netframework-4.8
Okay, so I'm sure this has been asked before but I can't seem to find anything in relation. I'm trying to create an assignment for school. It's a lunch order menu where you can place your order. We have to calculate the subtotal, the sales tax, and the order total. I can't seem to figure out the right code to use and I'm not 100% sure on what to try here.
public partial class Form1 : Form
{
decimal subtotal = 0m;
decimal salesTax = 0m;
decimal orderTotal = 0m;
public Form1()
{
InitializeComponent();
rdoBurger.Checked = true;
rdoPizza.Checked = true;
rdoSalad.Checked = true;
}
private void btnExit_Click(object sender, EventArgs e)
{
Close();
}
private void clearTotals()
{
}
private void btnPlaceOrder_Click(object sender, EventArgs e)
{
if (sender is RadioButton)
{
clearTotals();
}
if (rdoBurger.Checked)
{
decimal subtotal = 6.95m;
subtotal = Convert.ToDecimal(lblSubtotal.Text);
}
This is what I have but it's not showing the actual subtotal, it's still blank. What am I missing here?
It's not a bad start. This is more like what I'd expect to see:
private void btnPlaceOrder_Click(object sender, EventArgs e)
{
// only ONE of these can be checked, so "else if" is used
if (rdoBurger.Checked)
{
subtotal = 6.95m;
}
else if (rdoPizza.Checked)
{
subtotal = 5.95m;
}
else if (rdoSalad.Checked)
{
subtotal = 4.95m;
}
// multiple of these could be checked, so only "if" is used
if (checkBox1.Checked)
{
subtotal = subtotal + 100.00m; // whatever this item costs
}
if (checkBox2.Checked)
{
subtotal = subtotal + 4.99m; // whatever this item costs
}
if (checkBox3.Checked)
{
subtotal = subtotal + 10.99m; // whatever this item costs
}
// compute the tax and the total:
salesTax = subtotal * 0.0775m;
orderTotal = subtotal + salesTax;
// output it to your labels/textboxes?
lblSubtotal.Text = "$" + subtotal.ToString("F2");
lblSalesTax.Text = "$" + salesTax.ToString("F2");
lblOrderTotal.Text = "$" + orderTotal.ToString("F2");
}
Radio buttons can only have a single button selected at a time in a container. In this case it appears that the GroupBox is the container. If you had several groups of radio buttons you could use GroupBox as a container and have one radio button selected in each GroupBox. So, you cannot set all the radio buttons checked property to true.
In your btnPlaceOrder_Click the sender can not posibly be a radio button. The sender is the button that was clicked to run the event code.
private void button1_Click(object sender, EventArgs e)
{
//Find the radio button that is selected
RadioButton rButton = groupBox1.Controls.OfType<RadioButton>().FirstOrDefault(r => r.Checked == true);
switch (rButton.Text)
{
case "Hamburger - $6.95":
subTotal = 6.95m;
break;
case "Pizza - $5.95":
subTotal = 5.95m;
break;
case "Salad - $4.95":
subTotal = 4.95m;
break;
}
//Add code to handle Add-on items
//For example - The first check box is "Add Onions" - $0.50
if (checkBox1.Checked)
subTotal += .5m;
lblSubTotal.Text = subTotal.ToString();
decimal tax = subTotal * .0775m;
lblTax.Text = tax.ToString();
decimal total = subTotal + tax;
lblTotal.Text = total.ToString();
}
I'm trying to update values in read-only text boxes "Number of Months" and "Balance" after each click. In the given context both of these should have a starting value of 0 when the application begins. They should be updated after each click with the number of months simply adding up (1, 2, 3, etc.). The balance should show a specific value added up with each click.
The balance is equal to the monthly savings amount with the return of interest compounded monthly, so it would be dependent on the amount of months generated after each click.
I can make the month counter tick after each click as you'll see in the code, but I can't figure out how to update the balance and display it after each activation of the "Next Month" button.
public partial class frmSavingsCalculator : Form
{
public frmSavingsCalculator()
{
InitializeComponent();
}
int months = 0;
double balance = 0;
private void btnNext_Click(object sender, EventArgs e)
{
double monthlypmt;
int annualrate;
if (double.TryParse(txtMonthlySavings.Text, out monthlypmt))
{
if (int.TryParse(txtAnnualInt.Text, out annualrate))
{
annualrate = annualrate / 100;
double monthlyrate = annualrate / 12;
double changerate = (1 + monthlyrate);
txtMonths.Text = months.ToString();
months++;
balance = monthlypmt + Math.Pow(changerate, months);
txtBalance.Text = balance.ToString("c");
balance++;
}
else
{
MessageBox.Show("Please insert valid interest rate.");
}
}
else
{
MessageBox.Show("Please insert valid number.");
}
}
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
}
My form:
P.S. Here is the updated code, and yes I have additional problems!
namespace Assignment1_BTM380
{
public partial class frmSavingsCalculator : Form
{
public frmSavingsCalculator()
{
InitializeComponent();
}
int months = 0;
double balance = 0;
private void btnNext_Click(object sender, EventArgs e)
{
double monthlypmt;
int annualrate;
if (double.TryParse(txtMonthlySavings.Text, out monthlypmt))
{
if (int.TryParse(txtAnnualInt.Text, out annualrate))
{
double realannualrate = (double) annualrate / 100;
double monthlyrate = realannualrate / 12;
double interest = (monthlypmt * monthlyrate);
txtMonths.Text = months.ToString();
months++;
balance = monthlypmt + balance * (Math.Pow(1+monthlyrate,months));
txtBalance.Text = balance.ToString("c");
}
else
{
MessageBox.Show("Please insert valid interest rate.");
}
}
else
{
MessageBox.Show("Please insert valid number.");
}
}
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
SO, if you have the patience, bear with me. I managed to get the two text boxes to react each time I click the "Next Month" button, only I can't seem to get the appropriate values in the Balance. According to my professor's example, after 12 months, the Balance should display $619.86, only it shows $868.06 in my Form. I think the problem is with the Balance calculation, which I hope some of you can comment on.
Thanks to everyone who took the time to reply!
You're actually doing it right, the problem is that you're always getting the same result after the initial calculation due to a variable type problem.
The problem is the fact that the local variable "annualrate" is Integer.
Initially it makes sense since you are forcing them to write an integer number in the text box. Yet problem arises when you make it an actual rate by dividing it by 100.
annualrate = annualrate / 100;
Unless the rate in the text box is greater or equal to 100 you will always get a rate of zero because mathematically the result of any number between 0 and 100 (not including 100) divided by 100 is always between 0 and 1 (not including 1). For example, 50% rate should give you 0.5 but will result into just 0 because of the Integer division.
An easy fix will be:
...
double realannualrate = annualrate / 100;
double monthlyrate = realannualrate / 12;
...
You might also want to consider moving calculations and initialization to a separate functions
I wrote this program using Arrays in C#. It's homework. I pretty much have everything written in the program but I am stuck on clearing the array. I thought I had it but I don't understand where it's not working.
The program is pretty straightforward. The user enters a score and hits the "add" button. Then the user can enter more scores (anything 0 to 100). If the user chooses "Display" the program will sort the entered scores and display them in a messagebox (done)
if the user presses the "Clear Scores" button the program should clear out the scores. I have it written to clear the text boxes, and I also wrote in there "Scores.Clear();" (Scores being the name of my list array) and then I returned the focus back to my scores entry text box so the user can enter another score.
The book I am using simply says to clear type NameOfList.Clear(); so I'm stuck on why it's not clearing. I can tell it isn't because if I type more scores it will add the total instead of restarting.
Here is my full program code. My clear starts about halfway down.
Thank you in advance.
{
public partial class frmScoreCalculator : Form
{
//declare a list array for scores
List<int> Scores = new List<int>();
//set total and average to 0
int Total = 0;
decimal Average = 0;
public frmScoreCalculator()
{
InitializeComponent();
}
//calculate the average by dividing the sum by the number of entries
private decimal CalculateAverage(int sum, int n)
{
Average = sum / n;
return Average;
}
private void frmScoreCalculator_Load(object sender, EventArgs e)
{
}
//closes the program. Escape key will also close the program
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
//clears the text boxes, clears the array, returns focus back to the score text box like a boss.
private void btnClear_Click(object sender, EventArgs e)
{
txtScore.Text = "";
txtCount.Text = "";
txtTotal.Text = "";
txtAverage.Text = "";
Scores.Clear();
txtScore.Focus();
}
//makes sure the score is within the valid range, calculates the average, adds to the number of
//scores entered, and adds to the total
private void btnAdd_Click(object sender, EventArgs e)
{
if (txtScore.Text == string.Empty)
{
txtScore.Focus();
return;
}
int Score = int.Parse(txtScore.Text);
if (Score > 0 && Score < 100)
{
Scores.Add(Score);
Total += Score;
txtTotal.Text = Total.ToString();
txtCount.Text = Scores.Count.ToString();
Average = CalculateAverage(Total, Scores.Count);
txtAverage.Text = Average.ToString();
txtScore.Text = string.Empty;
txtScore.Focus();
}
// if number is not valid, ask user for valid number
else
{
MessageBox.Show("Please enter a number between 0 and 100.", "ENTRY ERROR, DO IT RIGHT!");
}
// returns focus to txtNumber
txtScore.Focus();
txtScore.Text = "";
}
//display button
private void btnDisplay_Click(object sender, EventArgs e)
{
//sorts the scores low to high
Scores.Sort();
//displays scores in message box
string DisplayString = "Sorted Scores :\n\n";
foreach (int i in Scores)
{
DisplayString += i.ToString() + "\n";
}
MessageBox.Show(DisplayString);
}
}
}
You need to zero the variable Total at the same time as clearing the array.
So I have a label called lblScore.Text and lblScore.Text = iCorrectACount.ToString(); where iCorrectACount is basically a counter of how many questions a user answered right. Now what I want to do is basically make it so that this number multiplies the end score depending on the difficulty chosen i.e. if easy questions are chosen, multiply iCorrectACount by 0 and cast to string, if medium questions are chosen, multiply iCorrectACount by 1.5 and cast to string and if hard questions are chosen, multiply iCorrectACount by 2 and cast to string, but I'm not sure how I'd do this.
My code is like this:
private void QuizReset()
{
// Resets the difficulty selection control and shows it again upon resetting the quiz
difficultySelectionControl.Reset();
difficultySelectionControl.BringToFront();
// Disabled the 'Next' button and prompts the user to select a difficulty - User cannot continue without choosing
btnNext.Enabled = false;
lblStatus.Text = "Please select a difficulty";
// Sets the number of questions and correct answers to zero
iCorrectACount = 0;
iCurrentQIndex = 0;
}
private void LoadQuestions(Difficulty difficulty)
{
// Defines a random variable to be used to shuffle the order of the questions and answers
var rand = new Random();
// Loads the corresponding XML document with 'Easy', 'Medium' or 'Hard' questions depending on difficulty chosen
var xdoc = XDocument.Load(GetFileNameFor(difficulty));
// List of questions that are filtered from the XML file based on them being wrapped in question tags
_questions = xdoc.Descendants("question")
.Select(q => new Question()
{
ID = (int)q.Attribute("id"),
Difficulty = (int)q.Attribute("difficulty"),
QuestionText = (string)q.Element("text"),
Answers = q.Element("answers")
.Descendants()
// Stores all answers into a string
.Select(a => (string)a)
// Randomizing answers
.OrderBy(a => rand.Next())
.ToArray(),
CorrectAnswer = (string)q.Element("answers")
.Descendants("correctAnswer")
// Use value instead of index
.First()
})
// Selects questions that match the difficulty integer of the option the user chose
.Where(q => q.Difficulty == (int)difficulty + 1)
// Randomizing questions
.OrderBy(q => rand.Next())
.ToList();
lblStatus.Text = String.Format("There are {0} questions in this section", _questions.Count);
}
private string GetFileNameFor(Difficulty difficulty)
{
switch (difficulty)
{
case Difficulty.Easy: return "quiz_easy.xml";
case Difficulty.Medium: return "quiz_medium.xml";
case Difficulty.Hard: return "quiz_hard.xml";
default:
throw new ArgumentException();
}
}
private void PickQuestion()
{
questionControl.DisplayQuestion(_questions[iCurrentQIndex]);
questionControl.BringToFront();
iCurrentQIndex++;
}
private void FormMain_Load(object sender, EventArgs e)
{
QuizReset();
lblScore.Text = "0";
}
private void miNewQuiz_Click(object sender, EventArgs e)
{
QuizReset();
lblScore.Text = "0";
}
private void miExit_Click(object sender, EventArgs e)
{
Close();
}
private void miHelp_Click(object sender, EventArgs e)
{
FormHowToPlay form = new FormHowToPlay();
form.ShowDialog();
}
private void miAbout_Click(object sender, EventArgs e)
{
AboutBox1 aboutForm = new AboutBox1();
aboutForm.ShowDialog();
}
private void btnNext_Click(object sender, EventArgs e)
{
if (iCurrentQIndex < _questions.Count)
{
PickQuestion();
lblStatus.Text = String.Format("Question {0} of {1}", iCurrentQIndex, _questions.Count);
}
else
{
btnNext.Enabled = false;
lblStatus.Text = String.Format("You answered {0} questions correctly out of a possible {1}",
iCorrectACount, _questions.Count);
this.Hide();
SummaryForm sumForm = new SummaryForm();
DialogResult result = sumForm.ShowDialog();
MenuForm mnuform = new MenuForm();
mnuform.ShowDialog();
}
}
private void difficultySelectionControl_DifficultySelected(object sender, DifficultySelectedEventArgs e)
{
iCurrentQIndex = 0;
LoadQuestions(e.Difficulty);
btnNext.Enabled = true;
}
private void questionControl_QuestionAnswered(object sender, QuestionAnsweredEventArgs e)
{
if (e.IsCorrect)
iCorrectACount++;
lblScore.Text = iCorrectACount.ToString();
}
It's the last little thing I need to figure out and I can't figure out how to get it so that if the difficulty = easy/medium/hard, multiply iCorrectAmount by 1/1.5/2/0.
Thanks for any help or advice.
Just do this:
int modifier = 1;
if (difficulty == Difficulty.Medium) { modifier = 1.5; }
if (difficulty == Difficulty.Hard) { modifier = 2; }
lblScore.Text = (iCorrectACount * modifier).ToString();
you'll need to get the difficulty from somewhere obviously, and I can't tell where exactly right now, but you have it because you passed it into the method LoadQuestions and GetFileNameFor, so just grab it, run the code, and BAM you got your modifier.
NOTE: I set the modifier to 1 by default, I'm pretty sure you didn't want to set it to 0 since that would net 0 as the result every time.
in difficultySelectionControl_DifficultySelected, store the selected difficulty in a class variable, m_difficulty.
Then, just access it in questionControl_QuestionAnswered
in your class definition, add private Difficulty m_difficulty.
in difficultySelectionControl_DifficultySelected, add a line saying m_difficulty = e.Difficulty.
then, you can use that difficulty in your questionControl_QuestionAnswered, just like #Michael Perrenoud suggested.