I'm an Amateur at C# and I don't understand what's going on here. It's not really a problem, as I can make a quickfix for it, but I still want to know Why.
Disclaimer: I know this is probably not the best way to design this program, but I have been given a very short amount of time to develop it and I'm just trying to ship it by the deadline.
I have a main form in my program. This form calls a second form when 1 of 2 buttons are pressed. These buttons use the same function to open the second form:
private void setupShow(int show)
{
fSetup setup = new fSetup(show);
setup.Show();
setup.FormClosed += new FormClosedEventHandler(setup_FormClosed);
}
When button1 calls that function, it hides the main form and opens the next form. However, when the second button calls that function it keeps the main form open, but still opens the second form.
So what could be causing this?
Button1 has quite a bit more code than button2 and if the functions are needed, I can post them here but it would fill up the majority of the screen.
Edit:
int show;
Is just a variable I use for functions performed in the second form. It has no bearing on anything as far as windows closing.
Button 1: Only calls this function. The function inserts into a database, then gets the inserted ID of that row and passes it to the second form
private void CheckFields()
{
OleDbCommand insertParty;
OleDbDataAdapter partyAdapt = new OleDbDataAdapter();
int nameL = PName.Text.Length;
int newPartyID = 0;
if (nameL > 0)
{
String test = "INSERT INTO Parties (PartyName, BackgroundImg) VALUES (?, ?)";
insertParty = new OleDbCommand(test, Parties);
insertParty.Parameters.AddWithValue("PartyName", PName.Text);
insertParty.Parameters.AddWithValue("BackgroundImg", tBrowse.Text);
Parties.Open();
insertParty.ExecuteNonQuery();
NewPartyForm.ActiveForm.Visible = false;
OleDbCommand selectnewParty;
OleDbDataAdapter newpartyAdaptr = new OleDbDataAdapter();
String selectNew = "SELECT TOP 1 PartyID, PartyName FROM Parties ORDER BY PartyID DESC";
selectnewParty = new OleDbCommand(selectNew, Parties);
OleDbDataReader newReader = selectnewParty.ExecuteReader();
while (newReader.Read())
{
newPartyID = newReader.GetInt32(0);
}
setupShow(newPartyID);
}
else
MessageBox.Show("Please Create a Party Name");
}
Button 2: Cuts out the integer from the string in a combobox and passes it to the second form
private void bLoad_Click(object sender, EventArgs e)
{
Object selectedParty = cLoadP.SelectedItem;
String sP = selectedParty.ToString();
String d1 = " - ";
char[] delim = d1.ToCharArray();
String[] numS = sP.Split(delim);
setupShow(Convert.ToInt32(numS[0]));
}
Like I said, this code is being developed by an amateur and very quickly. This is not the way I would normally do things, but I don't have much time to really think. lol
Second form initial function:
public fSetup(int partyID)
{
InitializeComponent();
pID = partyID;
lpID.Text += " " + pID.ToString();
}
I suspect that the cause of the different behavior is this line in the code for button 1:
NewPartyForm.ActiveForm.Visible = false;
There's no similar line in the code for button 2.
Related
I am with a problem where I have 3 forms and a class called Languages. The 3 Forms are the Main Form, The Settings Form and Languages Form. So what I need help with is, when I click in the language flag, I am trying to access to Main Form to pick up there a function I created there called 'changedLanguages()' I can access it, but when I click the flag, the Main Form is not Updating the changes without close the actual opened Main Form. But if i write a line of code mus.Show(); it opens a new one with the changes.
But I want to do the changes in the actual opened Main Form. Is it possible?
Below you have the LanguagesForm from the flag image click:
private void ptLang_img_Click(object sender, EventArgs e)
{
enLang_img.BackColor = Color.Transparent;
ptLang_img.BackColor = Color.CadetBlue;
Form1 mus = new Form1();
Languages PT_lang = new Languages();
mus.changeLanguage(PT_lang.file_PT, PT_lang.open_PT, PT_lang.settings_PT, PT_lang.exit_PT, PT_lang.controls_PT, PT_lang.play_PT, PT_lang.pause_PT, PT_lang.stop_PT, PT_lang.next_PT, PT_lang.prev_PT, PT_lang.playRecently_PT, PT_lang.volUp_PT, PT_lang.volDown_PT, PT_lang.help_PT, PT_lang.about_PT, PT_lang.faq_PT, PT_lang.update_PT);
mus.Refresh();
}
And here you have the function of 'changedLanguages()':
public void changeLanguage(string file, string open, string settings, string exit, string controls, string play, string pause, string stop, string next, string prev, string recPlayed, string volUp, string volDown, string help, string about, string faq, string update)
{
fileToolStripMenuItem.Text = file;
openToolStripMenuItem.Text = open;
settingsToolStripMenuItem.Text = settings;
exitToolStripMenuItem.Text = exit;
controlsToolStripMenuItem.Text = controls;
playToolStripMenuItem.Text = play;
pauseToolStripMenuItem.Text = pause;
stopToolStripMenuItem.Text = stop;
forwardToolStripMenuItem.Text = next;
backwardToolStripMenuItem.Text = prev;
playRecentToolStripMenuItem.Text = recPlayed;
volumeUpToolStripMenuItem.Text = volUp;
volumeDownToolStripMenuItem.Text = volDown;
helpToolStripMenuItem.Text = help;
aboutUMusicToolStripMenuItem.Text = about;
fAQToolStripMenuItem.Text = faq;
updatesToolStripMenuItem.Text = update;
}
Any help is welcome...
create a static instance of your main form, I hope that the changes will be immediately visible, if it does not work, add this statement after making your changes
Application.DoEvents();
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
I am new to C# and I am using windows forms.
I am building an application and I am facing a strange serious problem when Form loads.
I have 2 forms:
Form1 has a button_Edit and DataGridView
Form2 has a DataGridView1 and DataGridView2
As it is shown in the code and the screen shot, in Form1 when I select a row in DataGridView then click on Button_Edit the Order number and DateTime values in DataGridView on Form1 are passed to Form2 and then Form2 opens up.
Now in Form2 Load event there is a SQL query which takes Order number and DateTime to bring the relevant order details and then fill DataGridView1 and DataGridView2 in Form2.
In Form1:
Form2 frm2 = new Form2();
private void button_Edit_Click(object sender, EventArgs e)
{
frm2._ Order_Number= Convert.ToInt32(dataGridView1.SelectedRows[0].Cells[0].Value);
frm2._ Date_Time= Convert.ToDateTime(dataGridView1.SelectedRows[0].Cells[4].Value);
frm2.ShowDialog();
}
In Form2:
SqlConnection MyConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
SqlCommand MyCommand = new SqlCommand();
DataTable DataTable = new DataTable();
SqlDataAdapter Sql_Data_Adapter = new SqlDataAdapter();
int Order_Number;
DateTime Date_Time;
int i;
double Sum;
int RowIndex;
public int _ Order_Number
{
set { Order_Number = value; }
}
public DateTime _ Date_Time
{
set { Date_Time = value; }
}
private void Form2_Load(object sender, EventArgs e)
{
DataTable.Rows.Clear();
DataTable.Columns.Clear();
MyConnection.Open();
MyCommand.CommandText = "SELECT * FROM Customer_Order_Details WHERE Order_Number = #OrderNumber and Date_Time = #DateTime ";
MyCommand.Connection = MyConnection;
MyCommand.Parameters.Add("#OrderNumber", SqlDbType.Int).Value = Order_Number;
MyCommand.Parameters.Add("#DateTime", SqlDbType.DateTime).Value = Date_Time;
Sql_Data_Adapter.SelectCommand = MyCommand;
Sql_Data_Adapter.Fill(DataTable);
MyCommand.Parameters.Clear();
MyConnection.Close();
dataGridView1.Rows.Clear();
dataGridView2.Rows[0].Cells[1].Value = 0;
Sum = 0;
//////////////FILL THE ORDER INTO DATAGRIDVIEW1///////////
RowIndex = DataTable.Rows.Count - 1;
for (i = 0; i <= RowIndex; i++)
{
dataGridView1.Rows.Add(DataTable.Rows[i][2], DataTable.Rows[i][3], DataTable.Rows[i][4]);
// Calculate the total:
Sum = Convert.ToDouble(DataTable.Rows[i][4]) + Sum;
}
dataGridView2.Rows[0].Cells[1].Value = sum;
}
The issue:
This code works fine and as I wanted and DataGridView1 & DataGridView2 in Form2 are filled with the right details and it works fine when Form2 loads.
However, sometimes Form2 freezes after filling both DataGridView1 & DataGridView2 when Form2 loads and I can not do anything until I kill the Application using Task manager.
This issue happens sometimes and it is unpredictable. I really don’t know what is wrong.
I had a look here , here and here but non of those questions related to my issue. Note that I already use try catch and it does not throw anything because the form freezes. This freeze behavior occurs in the release mode I mean after I build EXE file then I install it in a PC and here the issue happens.
Has anyone got any idea why this bad unpredictable behavior occurs?
Is there anything that I should change in my code?
I will be very happy to listen to any new ideas/solutions no matter how small it is, it will be very beneficial.
Thank you
Do SQL work on another thread. Check out Asynchronous call.
BeginInvoke
I suggest using Async versions of Open (connecting to RDBMS) and ExecuteReader (query executing):
con.Open() -> await con.OpenAsync()
q.ExecuteReader() -> await q.ExecuteReaderAsync()
this easy substitution makes UI responsible (it doesn't freeze) while connecting to RDBMS and executing the query.
// do not forget to mark Form2_Load method as async
private async void Form2_Load(object sender, EventArgs e) {
// Do not share the connection - create it and dispose for each call
using (SqlConnection con = new SqlConnection(...)) {
await con.OpenAsync();
string sql =
#"SELECT *
FROM Customer_Order_Details
WHERE Order_Number = #OrderNumber
AND Date_Time = #DateTime";
// do not share command/query as well
using (SqlCommand q = new SqlCommand(sql, con)) {
q.Parameters.Add("#OrderNumber", SqlDbType.Int).Value = Order_Number;
q.Parameters.Add("#DateTime", SqlDbType.DateTime).Value = Date_Time;
dataGridView1.Rows.Clear();
dataGridView2.Rows[0].Cells[1].Value = 0;
Sum = 0;
// We actually don't want any sql adapter:
// all we have to do is to fetach data from cursor and append it to grids
using (var reader = await q.ExecuteReaderAsync()) {
while (reader.Read()) {
dataGridView1.Rows.Add(reader[2], reader[3], reader[4]);
Sum += Convert.ToDouble(reader[4]);
}
}
}
}
}
as said the code snippet you gave us isn't well written. You should use more Try/Catch Statements to prevent crashs and freezes on your programm. This will help you to find bugs even while running the Programm in release. Furthermore you have many options to solve your problem.
1:
Try to start Form2 in a second thread, then only your form2 will freeze until your sql finishes
2:
As mentioned before try to use asyncronous calls to avoid freeze time of handling the sql
3:
There is no direct need of a SQL Database/Connection. You could also use a Collection and create objects defined of your Products (like cola) and them bind them through the database. (If you are interested in i could show you a example)
The best way for you would be:
Add some Try/Catch Statements and get familiar with it, get familiar with Systems.Thread and try to start your form2 in a new thread if this doenst work go to step 2 and add asynchronus calls
In the end i would like to tell you, that namen your Forms "Form1" and "Form2" isn't nice could maybe you like to change it.
I have a problem that has been driving me nuts for days.. I've tried so many tutorials and code snippets from this and many other websites. I am building a P2P application and i have problems accessing the main thread.
Here is the simple flow of my application:
1. frmMain is shown - user clicks on login button
2. frmlogin is shown - user enters his name
3. after "logging in" - two threads are created (threadTCPlistener and threadUDPlistener)
4. frmDataGrid is shown
Server listen = new Server();
Thread listeningUDPThread = new Thread(new ThreadStart(listen.startUDPServer));
listeningUDPThread.IsBackground = true;
listeningUDPThread.Start();
Thread listeningTCPThread = new Thread(new ThreadStart(listen.startTCPServer));
listeningTCPThread.IsBackground = true;
listeningTCPThread.Start();
frmDataGrid dg = new frmDataGrid();
dg.Show();
5.Threads work in one separate class called "Server". In there they wait for incoming connections, and when TCP thread accepts a connection it starts receinving a file. Upon receiving the file, I would like to change the GUI in the frmDataGrid to add a new row to grid view. I've done something like this:
public void downloadFile()
{
//--receiving of the file--
frmDataGrid fdg = new frmDataGrid();
//filename is the name of received file, and 100's are just for testing (for now).
fdg.verifyUIRequest(fileName, 100, 100);
}
I am calling a method from frmDataGrid VerifyUIRequest that looks like this:
public void verifyUIRequest(string filename, int done, int percent)
{
if (dgvDown.InvokeRequired)
{
dgvDown.Invoke((MethodInvoker)delegate { updateDownDgv(filename, done, percent); });
}
else
{
updateDownDgv(filename, done, percent);
}
After this, the main thread should call the "updateDownDgv" method but the problem is that nothing is happening with my data grid. Here is the code for updating:
public void updateDownDgv(string filename, int done, int percent)
{
foreach (DataGridViewRow r in dgvDown.Rows)
{
if ((string)r.Cells[0].Value == filename)
{
r.Cells[1].Value = done;
r.Cells[2].Value = percent;
}
dgvDown.Invalidate();
return;
}
DataTable tab = (DataTable)dgvDown.DataSource;
DataRow row = tab.NewRow();
row[0] = filename;
row[1] = percent;
row[2] = done;
//MessageBox.Show(done.ToString());
tab.Rows.Add(row);
dgvDown.DataSource = null;
dgvDown.DataSource = tab;
}
I have tried doing this withh begin invoke, with some lambda expressions but nothing succeded. Can anyone please point me to an error or help in some other way? I would really appreciate it.
PS This is my first post, so if it is poorly formatted, i apologize in advance. :)
EDIT:
So the problem is obviously with instances, so I've done something like this:
from Server class where I create an instance of my frmDataGrid class, i now call it's constructor that takes 3 arguments.
frmDataGrid fdg = new frmDataGrid(fileName, 100, 100);
in that constructor, in frmDataGrid, I call verifyUIRequest. But then another error occurs, and I can't seem to figure it out. It stops at
if (dgvDown.InvokeRequired)
{...
error is as folows:
"Object reference not set to an instance of an object", i.e. NullReferenceException. What could be the error?
You are creating a brand new data grid in your downloadFile method. You should be updating the main grid and calling methods on that from your thread methods, not creating a new one that you drop on the floor when the downloadFile method exits.
I just started learning C#. I saw an old question of someone trying to make a coca-cola machine and it seemed like a good exercise.
But I got stuck on the money buttons. I can't figure out how I can store the amount of money a button represents in a variable, accessible by the ColaMachine method.
I've got the following code:
using System;
using System.Windows.Forms;
using System.Drawing;
namespace QuickSharp
{
public class ColaMachine : Form
{
public ColaMachine()
{
this.Text = "Cola Machine";
this.Size = new Size(450 , 500);
//Money & Money Buttons
Label Money;
Money = new Label();
Money.Text = "Insert Coins Here:";
Money.Location = new Point(20, 100);
this.Controls.Add(Money);
Button MoneyButton1;
MoneyButton1 = new Button();
MoneyButton1.Text = "€0,05";
MoneyButton1.Location = new Point(28,125);
MoneyButton1.Click += new System.EventHandler(this.MoneyButton1_Click);
this.Controls.Add(MoneyButton1);
Button MoneyButton2;
MoneyButton2 = new Button();
MoneyButton2.Text = "€0,10";
MoneyButton2.Location = new Point(28,165);
MoneyButton2.Click += new System.EventHandler(this.MoneyButton2_Click);
this.Controls.Add(MoneyButton2);
Button MoneyButton3;
MoneyButton3 = new Button();
MoneyButton3.Text = "€0,20";
MoneyButton3.Location = new Point(28,205);
MoneyButton3.Click += new System.EventHandler(this.MoneyButton3_Click);
this.Controls.Add(MoneyButton3);
Button MoneyButton4;
MoneyButton4 = new Button();
MoneyButton4.Text = "€0,50";
MoneyButton4.Location = new Point(28,245);
MoneyButton4.Click += new System.EventHandler(this.MoneyButton4_Click);
this.Controls.Add(MoneyButton4);
Button MoneyButton5;
MoneyButton5 = new Button();
MoneyButton5.Text = "€1,00";
MoneyButton5.Location = new Point(28,285);
MoneyButton5.Click += new System.EventHandler(this.MoneyButton5_Click);
this.Controls.Add(MoneyButton5);
Button MoneyButton6;
MoneyButton6 = new Button();
MoneyButton6.Text = "€2,00";
MoneyButton6.Location = new Point(28,325);
MoneyButton6.Click += new System.EventHandler(this.MoneyButton6_Click);
this.Controls.Add(MoneyButton6);
// Drinks & Drink Buttons
Label Drinks;
Drinks = new Label();
Drinks.Text = "Choose Your Drink:";
Drinks.Location = new Point(315 , 100);
Drinks.AutoSize = true;
this.Controls.Add(Drinks);
Button DrinkButton1;
DrinkButton1 = new Button();
DrinkButton1.Text = "Coca-Cola";
DrinkButton1.Location = new Point(328,125);
this.Controls.Add(DrinkButton1);
Button DrinkButton2;
DrinkButton2 = new Button();
DrinkButton2.Text = "Coca-Cola Light";
DrinkButton2.Location = new Point(328,165);
this.Controls.Add(DrinkButton2);
Button DrinkButton3;
DrinkButton3 = new Button();
DrinkButton3.Text = "Fanta";
DrinkButton3.Location = new Point(328,205);
this.Controls.Add(DrinkButton3);
Button DrinkButton4;
DrinkButton4 = new Button();
DrinkButton4.Text = "Sprite";
DrinkButton4.Location = new Point(328,245);
this.Controls.Add(DrinkButton4);
Button DrinkButton5;
DrinkButton5 = new Button();
DrinkButton5.Text = "Spa Blauw";
DrinkButton5.Location = new Point(328,285);
this.Controls.Add(DrinkButton5);
Button DrinkButton6;
DrinkButton6 = new Button();
DrinkButton6.Text = "Red Bull";
DrinkButton6.Location = new Point(328,325);
this.Controls.Add(DrinkButton6);
//Header & Machine Display
Label Header;
Header = new Label();
Header.Text = "Coca-Cola Machine";
Header.Font = new Font("Arial" , Header.Font.Size +5);
Header.ForeColor = Color.DarkRed;
Header.Location = new Point(132, 20);
Header.AutoSize = true;
this.Controls.Add(Header);
TextBox TextBox1 ;
TextBox1 = new TextBox();
if(InsertedCoins == 0.00)
TextBox1.Text = "Buy Your Ice Cold Drinks Here!";
else
TextBox1.Text = "Inserted Coins: €" + InsertedCoins;
TextBox1.BackColor = Color.Black;
TextBox1.ForeColor = Color.Red;
TextBox1.Font = new Font("Arial" , TextBox1.Font.Size +3);
TextBox1.ReadOnly = true;
TextBox1.Size = new Size(210,300);
TextBox1.Location = new Point(112,50);
// I tried to get the text scrolling here... :)
TextBox1.SelectionStart = TextBox1.Text.Length;
TextBox1.ScrollToCaret();
TextBox1.Refresh();
this.Controls.Add(TextBox1);
}
public double InsertedCoins;
// Money Button Click Events
private void MoneyButton1_Click(object sender, EventArgs e)
{
InsertedCoins = InsertedCoins + 0.05;
}
private void MoneyButton2_Click(object sender, EventArgs e)
{
InsertedCoins = InsertedCoins + 0.10;
}
private void MoneyButton3_Click(object sender, EventArgs e)
{
InsertedCoins = InsertedCoins + 0.20;
}
private void MoneyButton4_Click(object sender, EventArgs e)
{
InsertedCoins = InsertedCoins + 0.50;
}
private void MoneyButton5_Click(object sender, EventArgs e)
{
InsertedCoins = InsertedCoins + 1.00;
}
private void MoneyButton6_Click(object sender, EventArgs e)
{
InsertedCoins = InsertedCoins + 2.00;
}
private static void Main()
{
ColaMachine Scherm;
Scherm = new ColaMachine();
Application.Run(Scherm);
}
}
}
Also, if you have any tips on my general programming (e.g. to make things easier-to-follow for others trying to read my code), please tell me!
When I think about a Coke machine I see a button for each type of drink in the machine, but not buttons for different amounts of money. Maybe you mean a coke costs 50 cents so pressing the coke button I need to charge 50 cents.
Buttons and Event Handlers
When you press a button on the screen it generates a click event. You need to write a method to respond to that click. Any method that we use to respond to an event (generally speaking) is called an event handler. You must tell your program what buttons go with what event handlers. We call this registering the event handler
By convention, if your button is named 'CokeButton' then the event handler associated with that specific button would be named 'CokeButton_ClickHandler'. Or something like that.
General Advice
Think about the thing you are modeling and define things in code to reflect the real world. The things in your model typically will end up as classes, class properties, and class fields. What these things do typically end up as methods w/in the appropriate class. Then you think about how these things interact.
You do not need to figure out everything about a coke machine before you begin writing code. And you should write little bits at a time, test those and then build on what you've tested. Do not write oodles of complex-ish interacting code and then test. You'll end up spinning in circles chasing your tail. Write a little, test a little, repeat. Hear me now and believe me later; write a little, test a little, repeat. Heed this advice now and forever.
So here's how I might think about a Coke Machine. First there is a coke machine itself.
public class CokeMachine {}
A coke machine has a money slot, a return slot, and drink buttons. I can't really put money in a slot, so off hand, I'd say I'll type into a text box. Then I'll click a button and the coke will dispense. I feel like I've defined enough of the model to get started. There's lots of other things about a Coke Machine but I'm not going to worry about them right now.
But I need to know how much each drink costs.
Well, OK. then there must be "CokeCost", "7UpCost", etc. fields. So define them! We'll figure out how and where to use them as we go along.
public class CokeMachine {
Button Coke;
Button 7Up;
Button RootBeer;
TextBox MoneySlot;
double CokeCost = .75;
double 7UpCost = .65;
}
I said the buttons need handlers, so we can write some code shells at least. I expect they'll all work the same way so I'll focus on one for now. Note that as I write code I realize other things that must be dealt with. I'll put in comments, calls to methods that don't exist yet, etc.
public class CokeMachine {
Button Coke;
Button 7Up;
Button RootBeer;
TextBox MoneySlot;
double CokeCost = .75;
double 7UpCost = .65;
// "wiring up" the coke button click event to it's handler.
// We do this in C# by declaring an new EventHandler object (a .NET framework supplied class)
// and we pass in the name of our method as a parameter.
// This new EventHandler is *added* to the button's click event.
// An event can have multiple handlers, that's why we do "+="
// instead of just "=". Otherwise we would have accidentally "unhooked" any
// previously registered handlers.
Coke.Click += new EventHandler(Coke_ClickHandler);
// this is the .NET event handler method signature.
Public void Coke_ClickHandler (object sender, EventArgs args){
if (MoneySlot.Value >= CokeCost) {
DispenseDrink();
// How do I handle returning change? Maybe DispenseDrink() can do that.
}else {
// tell customer to put in more money
}
}
private void DispenseDrink() {
// An empty method is enough to get it to compile so for now that's fine.
// I need to test the Coke_EventHandler logic that I've written so far.
}
}
Now I need to test what I've written so far. After that I need to decide what to focus on next. But realize that when you're writing new code that depends on already written code, if that existing code has not been tested - and now you see errors, you've just made it much harder on yourself. You could have tested when the code is simpler. Now there's more, it's more complex, and will be harder to debug and fix.
Suggestions, Part II
At the risk of messing things up, I offer this folow-up to my original answer:
You can see that every drink button does the same thing, and given the above code we would write the same logic over and over for every button. If anything needs to change we have to change it everywhere.
More General Advice
One Object Oriented Progamming heuristic is encapsulate that which stays the same. You should always be on the lookout for places where things may be candidates for common code.
I want to emphasize that this common button behavior was not immediately obvious to me. Only after I wrote the code above did I get to thinking that all my drink button handlers would start to look the same and I realized that on a real drink machine they actually do behave the same way. My coding spidey-sense told it definitely is a good thing when the code reflects the identifiable behaviors of your real thing (pun intended!).
Refactoring
Actually is a technical term that means reworking existing code to make it more flexible, re-useable, readable, etc. In a word maintainable.
Refactoring should be in your thought processes all the time. But be sure you have a legitimate reason for making any change. Reshaping code is a normal, integral part of software development.
Let's refactor by extracting a method
Public void Coke_ClickHandler (object sender, EventArgs args){
PurchaseDrink("Coke", CokeCost);
}
// now we have a method that stands out and says THIS is how it works
// and a single point of change, rather than ump-teen button handlers.
private PurchaseDrink (string whatKind, double cost) {
// all I did so far is move the code and change "Cokecost" to "cost"
// Now I'm beginning to think I may need to pass "whatKind" to
// DispenseDrink() - but first I need to test the changes I've
// made at this level.
// ***** and since I already tested the code when I 1st wrote it,
// this refactoring will be easier & quicker to test.. GET IT??!! ******
if (MoneySlot.Value >= cost) {
DispenseDrink();
// How do I handle returning change? Maybe DispenseDrink() can do that.
}else {
// tell customer to put in more money
}
}
private void DispenseDrink() {
// An empty method is enough to get it to compile so for now that's fine.
// I need to test the Coke_EventHandler logic that I've written so far.
}
Enumerations
I hate using strings the way I used "Coke" above. Typo's and casing (upper/lower, that is) can cause problems that Visual Studio won't catch. When I have a limited list of things - kinds of drinks - I really like using enumerations. They show up in intellesense and I can use them in switch statements (and research the idea of "type safe"). And what I REALLY like is that they absolutely define in one place all the drink types our program knows about. It's like documentation!
You can store the amount for each button i buttons tag property, and use the following code in your eventhandler to read the amount:
void ValueButton_Click(object sender, EventArgs e)
{
Button button = sender as Button;
if (button == null) return;
if (button.Tag == null) return;
double amount = (double)button.Tag;
// Process the amount here....
InsertedCoins += amount;
}
First think:
You should divide the problem into two classes (class Test and class ColaMachine).
It looks like:
public class ColaMachine : Form
{
public ColaMachine()
{
...
}
}
public class Test
{
private static void Main()
{
ColaMachine Scherm;
Scherm = new ColaMachine();
Application.Run(Scherm);
}
}
Next one: If you want to return a variable that is private, use properties. IC will be a public method (properties). InsertedCoins will be a private variable.
public double IC
{
get
{
return InsertedCoins;
}
set
{
InsertedCoins = value;
}
}
Don't forget, that the machine has a lot of states. You should use the design Pattern, exactly State pattern.