Manipulating an object with a method in another class - c#

Hi I'm very new to c# and I was hoping someone could point me in the right direction. I have created a text box with the value "Total" set to 0 in my main class, and I have created a button "button1_Click" in my "AddFunds" class in which I want it to change the Total value by reading in what the user has in putted "Deposit" putting through a loop and incrementing the "Total". How do I get the AddFunds class to recognize the int Total in my main class?
public void textBox1_TextChanged(object sender, EventArgs e)
{
int Total = new int();
Total = 0;
////string str = Convert.ToString(Total);
////Total.Text = str;
}
public void button1_Click(object sender, EventArgs e)
{
for (int Deposit = 0; Deposit <= 0; ++Deposit)
{
Total = Deposit;
}
}
public void richTextBox1_TextChanged(int initialDeposit)
{
int Deposit = int.Parse(Console.ReadLine());
}

You have some ways of doing that, but remember if this classes you're talking about are forms, they need to exist simultaneously, anything else will end up on you trying to access a null reference.
You can create a public property Total on your main class and pass an instace of Main to AddFund
If the composition is the oposite as the above, and Main holds an instance of AddFund you can make Main inject itself on the other class or pass a Funciotn to AddFund so it can access the value
This one is the only one that doesn't sound like a code smell given the information you provided, abstract your logic to some classes that are not forms and manipulate them on the forms.

Related

How to fill an array from several textboxes?

I am trying to fill an array from several textboxes, then calculate with the values in the array.
I have the following code, but don't know why it doesn't work. Explained in the code:
private void button1_Click(object sender, EventArgs e)
{
double[] temperaturen = new double[6];
temperaturen[0] = double.Parse(textBox1.Text);
MessageBox.Show(temperaturen[0].ToString()); //the messagebox is just to test if
//it works
}
The above code works, but it stops working after I add more values from more textboxes:
private void button1_Click(object sender, EventArgs e)
{
double[] temperaturen = new double[6];
temperaturen[0] = double.Parse(textBox1.Text); //now it stops working and gives
temperaturen[1] = double.Parse(textBox2.Text); //the following error:
temperaturen[2] = double.Parse(textBox3.Text); //format exceptions was unhandled.
temperaturen[3] = double.Parse(textBox4.Text);
temperaturen[4] = double.Parse(textBox5.Text);
temperaturen[5] = double.Parse(textBox6.Text);
temperaturen[6] = double.Parse(textBox7.Text);
MessageBox.Show(temperaturen[0].ToString());
}
Does anybody know if parsing from textbox is the right way of doing this whatsoever? and why this does not work?
Please note, that you have 7 textboxes, not 6, so you should declare new double[7].
To avoid such errors, let's query with a help of Linq:
using System.Linq;
...
double[] temperaturen = new TextBox[] {
textBox1, textBox2, textBox3,
textBox4, textBox5, textBox6,
textBox7 }
.Select(box => double.TryParse(box.Text, out var value) ? value : double.NaN)
.ToArray();
Note, that text box can have text which is not a valid floating point value (say, "bla-bla-bla"),
I put double.NaN (Not a Number) in this case.
What actually solved the problem is:
Adding a try catch, and changing the array to have 7 values.
The final code, fully functioning.
private void button1_Click(object sender, EventArgs e)
{
decimal[] temperaturen = new decimal[7];//the array
try // try block
{
temperaturen[0] = decimal.Parse(txtZondag.Text);//parsing input
temperaturen[1] = decimal.Parse(txtMaandag.Text);//from
temperaturen[2] = decimal.Parse(txtDinsdag.Text);//textboxes
temperaturen[3] = decimal.Parse(txtWoensdag.Text);
temperaturen[4] = decimal.Parse(txtDonderdag.Text);
temperaturen[5] = decimal.Parse(txtVrijdag.Text);
temperaturen[6] = decimal.Parse(txtZaterdag.Text);
decimal temp = 10.2m;//decimal temp for calculations
decimal uitkomst1 = 0.0m;//decimal for sum.
uitkomst1 = temperaturen[0] - temp;//calculation
textBox8.Text = uitkomst1.ToString();
decimal uitkomst2 = 0;
uitkomst2 = temperaturen[1] - temp;
textBox9.Text = uitkomst2.ToString();
decimal uitkomst3 = 0;
uitkomst3 = temperaturen[2] - temp;
textBox10.Text = uitkomst3.ToString();
decimal uitkomst4 = 0.0m;
uitkomst4 = temperaturen[3] = temp;
textBox11.Text = uitkomst4.ToString();
decimal uitkomst5 = 0;
uitkomst5 = temperaturen[4] - temp;
textBox12.Text = uitkomst5.ToString();
decimal uitkomst6 = 0;
uitkomst6 = temperaturen[5] - temp;
textBox13.Text = uitkomst6.ToString();
decimal uitkomst7 = 0;
uitkomst7 = temperaturen[6] - temp;
textBox14.Text = uitkomst7.ToString();
}
catch (Exception exception)//catch block
{
MessageBox.Show("Incorrecte invoer! " + exception.Message);
}
}
Your code is not SOLID, meaning that it doesn't follow the guidelines of the Solid principle. If you are not familiar with SOLID, consider to read some background information about SOLID
For instance, your code has more than one Responsibility. This principle states that each class should have a single purpose or responsibility. This helps reduce bugs by:
Simplifying the code,
Making testing easier, and
Allowing for more modularity (i.e., the ability to reuse and combine code).
In your case: you need a class that can read the temperature from somewhere. This can be from a text box, or from the internet, a digital thermometer, anything:
interface IThermometer
{
double ReadTemperature();
}
class TextBoxThermometer : IThermometer
{
public TextBox TextBox {get; set;}
public double ReadTemperature()
{
// TODO: add some error checking: null textbox, no proper value in the textbox, etc.
return Double.Parse(this.TextBox.Text);
}
}
This class is easy to create, easy to test, and easy to maintain: if you want a decimal as temperature, the changes will be minimal.
Once you've got this class, and created unit tests for them, you can be certain that the class is working correctly, for any TextBox that you attach to this class.
Apparently you don't have one Thermometer, you've got a collection of Thermometers. From each Thermometer you can read the temperature:
class ThermometerCollection : IReadOnlyCollection<IThermometer>
{
public List<IThermometer> Thermometers {get; set;}
// TODO: implement IReadOnlyCollection<IThermometer>
public double ReadTemperatur(int thermometerIndex)
{
// TODO: check validity of thermometerIndex
return this.Thermometers[thermometerIndex].ReadTemperature();
}
}
The thermometer collection is also a fairly simple class with functions with only one or two lines. Easy to understand, Easy to test, easy to maintain. If desired you can even have different types of thermometers in the collection: one that reads from a text box, another that reads from the internet.
Because you've unit tested the class, you can be certain that it works with all kinds of IThermometers.
Inside your form:
private IReadOnlyCollection<IThermometer> Thermometers {get;}
public MyForm() // constructor
{
InitializeComponent();
this.Thermometers = new ThermometerCollection
{
Thermometers = new List<IThermometer>()
{
new TextBoxThermometer() {TextBox = this.TextBox1},
new TextBoxThermometer() {TextBox = this.TextBox2},
new TextBoxThermometer() {TextBox = this.TextBox3},
new TextBoxThermometer() {TextBox = this.TextBox4},
new TextBoxThermometer() {TextBox = this.TextBox5},
new TextBoxThermometer() {TextBox = this.TextBox6},
},
}
}
Now your form has one ThermometerCollection with six thermometers. For users of these thermometers, it is not important how they get their temperature. They might read them from a text box, but if in future you decide to read the temperature from the internet, changes in your form will be minimal. Remember: because you've got unit tests for your TextBoxThermometer you can be sure that this type of Thermometer works flawless.
Now that you've decoupled the Thermometer from where it gets the temperature, in your form you can write methods to read the temperature
public double ReadTemperature(int thermometerIndex)
{
return this.ThermometerCollection.ReadTemperature(thermometerIndex);
}
Again a one liner method. Note that this is a method in your Form, so it is fairly difficult to unit test. But since you have tested the ThermometerCollection thoroughly, you hardly miss the test.
private void button1_Click(object sender, EventArgs e)
{
this.ProcessTemperatures();
// TODO: do other things that must be done when button1 is clicked
}
public void ProcessTemperatures()
{
// Read the temperatures, and process them:
ICollection<double> measuredTemperatures = Enumerable.Range(1,6)
.Select(thermometerIndex => this.ReadTemperature(thermometerIndex)
.ToList();
this.ProcessTemperatures(measuredTemperatures);
}
public void ProcessTemperatures(ICollection<double> measuredTemperatures)
{
// TODO: do what you wanted to do with your measured temperatures
}
I've decoupled the button click from the processing. If later you decide that you want to do something different if the button is clicked, changes will be minimal. Also, if you want to add a menu item that will also process the temperatures: the procedure will be a one liner.
Summary
Because I made methods that only have one specific task, you have methods that are easy to understand, easy to unit test, simple to change and maintain, simple to reuse.
Because I separated your from from the notion of Thermometers, you can reuse the thermometers in a different form. You have unit tested the Thermometer classes, so you can be certain that it will also work if you want 10 Thermometers.
Because I separated the Thermometer from where it gets its data from, it is easy to use a different type of Thermometer, for instance one that reads from a cell in a table, or from the internet. Users of your Thermometer class won't notice the difference.

Declare global variables in codebehind file

In the code below, how do I declare totalSquareFeet and other variables global so that when I parse data from asp.net textboxes it becomes accessible to other methods specifically performAnalysisButton button method?
Note that the code below is not complete. I have deleted most of the code to keep my question as simple as possible. To give you a background following are the purposes of 2 buttons:
okButton_Click: Take the data from textboxes (created in asp.net webform) and perform some calculations and display results in labels.
performAnalysisButton_Click: Take the data from aforementioned textboxes and perform some calculations and display results in another textbox.
In short, all the local variables that I have declared under the okButton_Click method, I would like to use them under performAnalysisButton_Click method. I was wondering if there is a way I can avoid parsing the textbox data again?
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Create objects for fiscal year end dates (e.g. 12/31/17)
FiscalYearEnds fiscalYearEnds = new FiscalYearEnds();
firstFiscalYearEndLabel.Text = String.Format("{0:MM/dd/yyyy}", fiscalYearEnds.firstFiscalYearEndDate());
secondFiscalYearEndLabel.Text = String.Format("{0:MM/dd/yyyy}", fiscalYearEnds.secondFiscalYearEndDate());
}
protected void okButton_Click(object sender, EventArgs e)
{
// Parse values
int totalSquareFeet = int.Parse(totalSqFtTextBox.Text);
int totalOccupiedSquareFeet = int.Parse(occupiedSqFtTextBox.Text);
decimal grossRentsFirstYear = decimal.Parse(grossRentsFirstYearTextBox.Text);
decimal grossRentsSecondYear = decimal.Parse(grossRentsSecondYearTextBox.Text);
int loanAmount = int.Parse(loanAmountTextBox.Text);
double interestRate = double.Parse(interestRateTextBox.Text);
int loanTerm = int.Parse(yearlyLoanTermTextBox.Text);
}
protected void performAnalysisButton_Click(object sender, EventArgs e)
{
Analysis analysis = new Analysis();
analysis.GrossRentFirstYear = decimal.Parse(grossRentsFirstYearTextBox.Text);
analysis.GrossRentSecondYear = decimal.Parse(grossRentsSecondYearTextBox.Text);
analysisAndComments.Text = String.Format("{0}", analysis.PerformAnalysis());
}
}
After roundtrip the page returns to the client and there will be no information saved on the server unles you save it somewhere. Meaning you can hold the extra information on the page inside a hiddenfield or you can store it inside a Session or ViewState
Session This will be saved on the server(Also available if you go to other page)
Session["totalSquareFeet"] = totalSquareFeet;
Then you can read it back in performAnalysisButton_Click
int totalSquareFeet = (int)Session["totalSquareFeet"];
if you wanna clean the Session var after leaving page you can do
Session["totalSquareFeet"] = null;
ViewState This will be saved on the page(Credits to Alexandru Popa)
ViewState["totalSquareFeet"] = totalSquareFeet;
Read
int totalSquareFeet = (int)ViewState["totalSquareFeet"];
Hiddenfield This will be saved on the page
store it in a <asp:HiddenField ID="HiddentotalSquareFeet" runat="server" />
HiddentotalSquareFeet.Value = totalSquareFeet.ToString();
And read it back
if(int.TryParse(HiddentotalSquareFeet.Value, out int totalSquareFeet)){
}
If I understood your question right...
To declare a variable as global, all you need to do is to declare it outside of the function and inside of the class that you are working in:
public partial class Default : System.Web.UI.Page
{
int totalSquareFeet = 0;
protected void Page_Load(object sender, EventsArg e)
{ // And the rest of the code should be the same...
}
// Same code as you had...
}
So, it's al about removing the declaration from within the function to the outside.
I solved the problem by declaring properties at the class level and that became my global variables which I can access in button methods. Now I don't need to parse textbox values again in the button methods. See example below:
public int totalSquareFeet
{
get { return int.Parse(totalSqFtTextBox.Text); }
set { int.Parse(totalSqFtTextBox.Text); }
}

Windows Forms - Integer being reset upon form load

My issue is that an integer created in my main class,
public int loadCountGold = 0;
that is incremented to when a button triggering the appearance of another form
public void goldButton_Click(object sender, EventArgs e)
{
loadCountGold += 5;
Console.WriteLine(loadCountGold);
GoldForm gForm = new GoldForm();
gForm.Show();
}
Is not having its incremented value carried over to the form where it needs to be used in an if statement.
private void GoldForm_Load(object sender, EventArgs e)
{
//Sets the random (within reason) value for gold
if (main.loadCountGold <= 1)//if its the firstload of the form
{
Console.WriteLine(main.loadCountGold);
Random rand = new Random();
currentGoldValue = rand.Next(1200, 1350);
}
}
I had included a few write lines at points before the form is opened, and one within the problem form to see what was happening to the value. This can be seen below
It shows the output that my program is giving, highlighted are the outputs from my "Main" class and the other are the ones being produced from the problem form.
The aim is to have the value incremented each time the form is opened so that the code within the if statement is only run the first time the form is opened, but currently it is running every time. (I am aware I would need to change the amount it is incremented)
In your main form, call GoldForm like this:
GoldForm gForm = new GoldForm(this);
In the constructor of GoldForm, do it like this:
Main main;
public GoldForm(Main main)
{
InitializeComponent();
this.main = main;
}
Now you don't create a new instance of Main every time you click the button.
Output after 3 button clicks:
5
10
15
This is an issue of needing a static variable rather than one instanced with the class. The easy answer is to change your integer declaration to:
public static int loadCountGold = 0;
now all references to this variable will point to the same thing.
I should specify that your reference in the later form needs to be Main with a capital M.
if (Main.loadCountGold <= 1)//if its the firstload of the form

Creating Calculator with Class - C#, However unable to solve error

I'm trying to create a Calculator with a Class. However using references from the internet particularly from this website (https://www.sourcecodester.com/tutorials/c/7548/simple-calculator-using-class-c.html)
It did not mention to declare "Information" or whatsoever.
When I typed in the code, the error list return with Information does not exist in current context.
Is there a way to modify the code below? Thank you so much.
public partial class Form4 : Form
{
public Form4()
{
InitializeComponent();
}
private void Form4_Load(object sender, EventArgs e)
{
}
public void RadioButton_Click(object sender, System.EventArgs e)
{
//call a constructor method and return to cal as an instance of a class
calculate cal = new calculate();
//declaring the string variable represent as a textbox
string txtnum1 = TextBox1.Text;
string txtnum2 = TextBox2.Text;
//declaring the double variable
double dbl_val1 = default(double);
double dbl_val2 = default(double);
if (**Information**.IsNumeric(txtnum1) && **Information**.IsNumeric(txtnum2)) //check if the textbox has a numeric value
{
//convert the string to double
dbl_val1 = double.Parse(txtnum1);
dbl_val2 = double.Parse(txtnum2);
//get the value of the converted variable
//to pass it into the variable in the class
cal.num1 = dbl_val1;
cal.num2 = dbl_val2;
//the condition is, if the radiobutton is clicked,
//the operation of MDAS executes.
if (Radio_Multiplication.Checked)
{
//result:
cal.multiply(); //call a subname in a class for multiplying
}
else if (Radio_Addition.Checked)
{
//result:
cal.add(); //call a subname in a class for adding
}
else if (Radio_Subtraction.Checked)
{
//result:
cal.subtract(); //call a subname in a class for subtracting
}
}
else
{
//the result is:
//if the textbox is empty or has a string value
TextBox3.Text = "Enter a number";
return;
}
//put the result of the MDAS to a textbox.
TextBox3.Text = cal.total.ToString();
}
}
I had a quick look at the link and they don't appear to have declared Information anywhere nor have they indicated that they've overridden anything so...I don't know.
That line, however, is just validating that the information entered into the two text boxes are actually numbers and not anything else that can't be calculated.
There are lots of methods you could use to check those numbers. Options would include, but are not limited to:
if(Int32.TryParse(txtNum1, out int temp1) && Int32.TryParse(txtNum2, out int temp2))
{
do stuff;
}
or
if(txtNum1.All(char.IsDigit) && txtNum2.All(char.IsDigit))
{
do stuff;
}
There are other options, but those two might be worth looking into.
Downloading the sample project, I had a look at what Information refers to. Turns out, it's a class from the Microsoft.VisualBasic namespace, presumably for exposing certain aspects of the VB core library to all .NET languages. You can use it in your program by adding a reference to Microsoft.VisualBasic to your project and adding:
using Microsoft.VisualBasic;
to the top of your code file.
(Personally, I can't imagine that this approach is terribly efficient. It's supposed to take an object and determine if it can be evaluated as a number, and I have no idea what approaches it uses to make that deduction based on any random object. You would probably be better off using one of the alternatives that Benny O'Neill suggests.)

C# Take combobox item from one form and add its name as text to another

Ok so I'm attempting to create a simple game. In a nutshell it's a resource management game where the player will attempt to manage a thieves guild. In regards to running missions I've created a Thief class, a new instance of which is created when a new thief is recruited. I have coded within the thief class the ability to gain experience and level up.
Here's my specific problem:
I want the player to be able to select which thief/thieves to send on a mission. I have thought about it and figured that opening a new form and populating it with checkboxes is the easiest way to allow this. These checkboxes will be related to a List<thief> of thieves, the player then checks the thieves s/he wants to send and these are then stored in another List<thief> and passed on to the run mission function.
I've built a separate project with the intention of testing and playing around with this before putting it into the main program. The test project consists of two forms: The first (frmMain) with a textbox to hold the selected options and a button to open the second form (frmSelect). Currently I can open and populate the second form (frmSelect) but when I try to add the checked options to the textbox I simply...well can't.
So far I have tried directly accessing the textbox by typing frmMain.txtOptionsDisplay in the cs file of frmSelect but it causes the following error:
An object reference is required for the non-static field, method or
property
I tried to create a new form in frmSelect and make it equal to the active instance of frmMain with: Form frmTemp = frmMain.ActiveForm; and then alter the textbox using frmTemp as a go-between but that produced the error:
'System.Windows.Forms.Form' does not contain a definition for
'txtOptionsDisplay'.
Having searched both google and stackoverflow forums I've encountered answers that I either have never heard of (Threading) or answers that I kind've recognise but can't interpret the code pasted to make it relevant to my problem (delegates).
Any advice or pointers would be fantastic.
EDIT:
frmMain code:
public frmMain()
{
InitializeComponent();
selections.Add("Option 1");
selections.Add("Option 2");
}
private void btnClick_Click(object sender, EventArgs e)
{
frmSelectOptions.Show();
int length = selections.Count();
for (int i = 0; i < length; i++)
{
CheckBox box = new CheckBox();
box.Text = selections[i];
box.AutoSize = true;
box.Location = new Point(50, 50*(i+1));
frmSelectOptions.grpControls.Controls.Add(box);
}
}
public void updateText(string option)
{
txtOptionsDisplay.Text += option;
}
}
frmSelect code:
public List<CheckBox> selectedOptions = new List<CheckBox>();
Form frmTemp = frmMain.ActiveForm;
public frmSelect()
{
InitializeComponent();
}
private void btnSelect_Click(object sender, EventArgs e)
{
foreach (CheckBox box in grpControls.Controls)
{
if (box.Checked == true)
selectedOptions.Add(box);
}
this.Hide();
}
}
I hope this formats correctly... I'm kinda new and don't know how to indent. Oh look there's a preview...
Does this help?
Your problem is that controls defined within a form by default receive the private access identifier. Hence you could just add a property along the lines of
public ControlType ProxyProperty {
get {
return txtOptionsDisplay;
}
}
Besides from that you should think about wether what you're trying is actually a good solution. Manipulating forms from one to another will become a huge clusterfuck in terms of maintenance later on.
I'd suggest using the Singleton pattern for your frmMain. This will help safeguard you from accidentally launching another instance of frmMain and at the same time, will give you access to frmMain's objects. From there, you can either write accessors to Get your txtOptionsDisplay or you can make it public. Below is an example:
public class frmMain
{
private static frmMain Instance = null;
private static object LockObj = new object();
public static frmMain GetMain()
{
// Thread-safe singleton
lock(LockObj)
{
if(Instance == null)
Instance = new frmMain();
return Instance;
}
}
public string GetOptionsDisplayText()
{
return txtOptionsDisplay.Text;
}
}
public class frmSelect
{
private void frmSelect_Load(object sender, EventArgs e)
{
// Set whatever text you want to frmMain's txtOptionsDisplay text
txtDisplay.Text = frmMain.GetMain().GetOptionsDisplayText();
}
}
If you do go this route, don't forget to update Program.cs to use frmMain's singleton.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new frmMain()); - Old method
Application.Run(frmMain.GetMain());
}

Categories