Creating custom Label in C# and passing data in events - c#

I had been playing around with an idea for a game, and implementation was going fairly well, but I have hit a stumbling block.
Basically, I have a form, which will show talent trees. I am just going to use labels to display the relevant details, and I want to create them programmatically. The display part is working fine, the part I am having trouble with is adding an event handler to the labels.
I want to be able to pass data during the event handling, so that I can identify which specific label was clicked, but I am hitting a brick wall. So when a particular label is clicked, the name of its associated skill (just passing a string) will be sent to the event handler. Any help would be appreciated. Here is the relevant code that I have:
public void DisplayTree()
{
int i=0;
startPoint.X = 40;
startPoint.Y = 125;
foreach(SkillNode s in tree.tier1)
{
for (i=0; i < s.labels.Count;i++ )
{
//Displays a label for each available rank for a skill
s.labels.ElementAt(i).Text = (i+1).ToString()+"/"+s.maxRank.ToString();
s.labels.ElementAt(i).Location = startPoint;
startPoint.Y += s.labels.ElementAt(i).Height + 2;
s.labels.ElementAt(i).Name = "lbl"+s.name+i.ToString();
//Only enable it to be clicked if the user is at the correct rank
if (s.rank == i)
{
s.labels.ElementAt(i).Enabled = true;
}
else
{
s.labels.ElementAt(i).Enabled = false;
}
//Add Event here
//I want to pass the name of the skill with the event
this.Controls.Add(s.labels.ElementAt(i));
}
startPoint.X += s.title.Width + 5;
startPoint.Y = 125;
}
}
public void LabelClick()
{
//Code here to pick out the name of the label
}

Try this:
public void LabelClick()
{
Console.WriteLine(((Control)sender).Name);
}

When you create an event and want to follow the official C# styleguide, you follow the following pattern:
public delegate void {YourName}EventHandler(object sender, {YourName}EventArgs args);
public event {YourName}EventHandler EventName;
Every information about what happened in the event or can be manipulated by the subscriber is stored in a class that inherits EventArgs. The delegate also contains a reference to the sender, which is the object that fires the event.
When you fire an event you do the following, regularly in a protected method that has the same name as the Event with an "On" as prefix:
EventName?.Invoke(this, new {YourName}EventArgs() { Initialize Stuff });
As you can see, you can work with the sender and identify the object. In your case you could also change object sender to UIElement sender (or similar) to make it easier to identify stuff without a cast.

Related

WinForm C# how attach an Method to a Control programmaticaly created

In my apps, I generated a bunch of DataGridView programmaticaly. I can add/remove rows to it and retrieve all the data from it and copy the data in another DataGrid.
for instance when Im creating it
public void Example(TabControl tab)
for(int i=0;i<tab.TabCount;i++)
{
tab.TabPages.Add("Panneau " + tab.TabCount);
DataGridView panGridView = new DataGridView();
panGridView.Name = "panGridView_" + tab.TabCount;
panGridView.Location = new System.Drawing.Point(0, 0);
panGridView.RowTemplate.Height = 24;
panGridView.Size = new System.Drawing.Size(1375, 458);
panGridView.Columns.Add("id", "id");
panGridView.Columns.Add("part_code", "part_code");
panGridView.Columns[0].Width = 100;
panGridView.Columns[1].Width = 150;
panGridView.Visible = true;
panIndex.Items.Add(tab.TabCount - 1);
tab.TabPages[tab.TabCount - 1].Controls.Add(panGridView);
}
Now, I want to attach Method to this Control. I think the best way would be to attach it when I initialize the Control. For example I would attach a Method like this one
public void Action(DataGridViewCellMouseEventArgs e)
{
if(e.RowIndex==2)
{
MessageBox.Show("Hello");
}
I tried a lot of this but cant figure out how do it.
Thanks
It seems to me that you want a special kind of DataGridView. You want a DataGridView with an attached method. You've learned, whenever you need a "class, very similar to another class, but with just a small thing different", you need to create a derived class, or make a composition, if you don't want to expose all methods of the base class.
class DataGridViewWithAttachedMethod : DataGridView // TODO: invent proper name
{
...
}
class MySpecialDataGridView : UserControl
{
private DataGridView dgv1;
...
}
The advantage of the first method is that users of your class (= code, not operators) have access to all DataGridView methods, so it will be very flexible to use. Disadvantage: they have access to all DataGridView methods, so they can mess up your DataGridView.
Whether you will use derivation or composition depends on how fool proof your class needs to be, in other words: do you want to expose methods that you prefer not to be used by others?
I want to attach Method to this Control.
This is not really clear. Do you want to give DataGridView an extra method, always the same one? Or do you want to Dynamically attach a method: dgv1 has another attached method than dgv2.
class DgvWithExtraMethod : ...
{
public void Action(DataGridViewCellMouseEventArgs e)
{
if(e.RowIndex==2)
{
MessageBox.Show("Hello");
}
}
}
All instances of this dgv will have the same extra method. All you have to do is create an object of this class, and you will have this method.
However, if you want to attach different methods to instances of the class, you need a property that contains this method.
class DgvWithMethod : ...
{
public Action<DataGridViewCellMouseEventArgs> ExtraMethod {get; set;}
}
If you want your class fool proof, consider to initialize the method with a "no operation (NOP)"
private static Action<DataGridViewCellMouseEventArgs> NOP = (e) => {};
public Action<DataGridViewCellMouseEventArgs> ExtraMethod {get; set;} = NOP;
Usage:
DgvWithMethod dgv1 = new DgvWithMethod
{
ExtraMethod = (e) =>
{
if(e.RowIndex==2)
{
MessageBox.Show("Hello");
}
}
}
This is the exact answer to your question. However, what I think that you want to know is: if the operator click on row 2, then I want to execute method F(), and if he clicks on row 3, I want to execute method G(), etc
If that is what you want, use visual studio designer to add an event handler on DataGridView.CellMouseClick, or if you want to reuse this class (derivation / composition) override DataGridView.OnCellMouseClick.
private void DataGridView1_CellMouseClick(Object sender,
DataGridViewCellMouseEventArgs e)
{
// find out which column is clicked
switch (e.ColumnIndex)
{
case 0: // column Id clicked
this.ProcessColumnIdClick(e);
break;
case 1: // column Name clicked
this.ProcessColumnNameClick(e);
break;
...
Take care though: if you allow column reordering, you should compare ColumnIndex with the DisplayIndex of each column.
if (e.ColumnIndex == this.columnId.DisplayIndex)
this.ProcessColumnIdClick(e);
else if (e.ColumnIndex == this.columnName.DisplayIndex)
...
Like #CurleD stated, you simply subscribe your method to specific EventHandler. So change your Action to this:
private void panGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if(e.RowIndex==2)
{
MessageBox.Show("Hello");
}
}
and then subscribe to the CellMouseClick event:
panGridView.CellMouseClick += panGridView_CellMouseClick;

Load popup form cross thread

I'm having trouble manipulating forms when from another thread.
I've overcome the issue by loading the form at runtime, showing it then hiding it. This means the form is created on the right thread and can be manipulated using invokes.
This is not the right way to do it. I have 3 problems that come from using this method
I can't spawn another popup box I have to use the one I created at runtime
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
I have to use a variable bool to hold if the popup is open or not.
If anyone could point me in the right direction It would be much appreciated. Currently my code looks like:
On Main form Load:
CallerIDfrm = new frmCallerID();
CallerIDfrm.Show();
CallerIDfrm.Hide();
to manipulate the popup Im using
delegate void StringArgReturningVoidDelegate1(string CallerIDnum, string CallerIDname, string ContactID);
private void CallerID(string CallerIDnum, string CallerIDname, string ContactID)
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate1 d = new StringArgReturningVoidDelegate1(CallerID);
CallerIDfrm.Invoke(d, new object[] { CallerIDnum, CallerIDname, ContactID });
}
else
{
if (ContactID != null || ContactID != "0")
{
CallerIDfrm.ContactID = ContactID;
}
CallerIDfrm.Mainfrm = this;
CallerIDfrm.TopLevel = true;
CallerIDfrm.TopMost = true;
CallerIDfrm.lblCallerIDname.Text = CallerIDname;
CallerIDfrm.lblCallerIDnum.Text = CallerIDnum;
CallerIDfrm.Show();
CallerIDOpen = true;
}
}
To Hide the popup until required again im using:
delegate void StringArgReturningVoidDelegate2();
private void CallerIDClose()
{
if (CallerIDfrm.InvokeRequired)
{
StringArgReturningVoidDelegate2 d = new StringArgReturningVoidDelegate2(CallerIDClose);
CallerIDfrm.Invoke(d, new object[] { });
}
else
{
try
{
CallerIDfrm.Hide();
CallerIDOpen = false;
}
catch
{
}
}
}
I've tried otherways but the Popup loads as if it is not responding and I loose access to the popup.
Ultimately I'd like to be able to spawn multiple popups and have the ability to close them from the Main Form.
What I gather from your question: You have an caller api/lib/class and you like to show CallerId on a popup form when a call is received. Have a look at Events and Event Driven programming.
The following codes has not been tested, I wrote it from top of my head. Might not compile, they are here to show an example:
Create an CallReceived event in api/lib class as follows:
public event EventHandler<CallReceivedEventArgs> CallReceived;
protected void OnCallReceived(EventArgs e)
{
var handler = CallReceived;
if (handler != null)
handler(this, e);
// Note: For C# 6.0 and later, above statements can be simplified to
// CallReceived?.Invoke(this, e);
}
Note: If you don't have access to this api/lib code, create a Gateway class and put your event in there along with mechanism to trigger it.
Also create a CallReceivedEventArgs, this will be used to transfer event data:
public class CallReceivedEventArgs : EventArgs
{
public string CallerIDnum {get; set;}
public string CallerIDname {get; set;}
public string ContactID {get; set;}
}
Now, in your api/lib class raise this event whenever a call is received:
// a call received, replace dummy values with actual values
OnCallReceived(new CallReceivedEventArgs() { CallerIDnum="5554443322", CallerIDname="SOME_NAME", ContactID="SOME_CONTACT" });
Finally in your GUI form, register to said event and process accordingly.
// inside your main form class
private CallerAPI callerApi = new CallerAPI();
// somewhere inside you main form class, register to event
// from your use case, I would place it inside Main Form's constructor
callerApi.CallReceived += callerApi_Callreceived;
// receive event
void callerApi_Callreceived(object sender, CallReceivedEventArgs e)
{
var callerIDnum = e.CallerIDnum;
// etc.
// show callerId form with details
// you need to change frmCallerID's constructor accordingly
CallerIDfrm = new frmCallerID(e.CallerIDnum, CallerIDname, ContantID);
// to be able to track opened popups, you can store them inside a list
// private List<Form> openPopupList = new List<Form>();
//
// alternatively, you can assign CallerIDnum to form's name property
// and store these inside a List<string> instead of List<Form>
openPopupList.add(CallerIDfrm);
CallerIDfrm.Show();
}
Don't forget to unregister from event.
callerApi.CallReceived -= callerApi_Callreceived;
To wrap it up:
I can't spawn another popup box I have to use the one I created at runtime
You can create and show multiple frmCallerID, independent from each other.
The forms flash briefly on load - now that I have 3 forms its pretty obvious what I'm doing.
Since new approach creates CallerID forms based on events, you won't see these form flashing. It'll open whenever a CallReceived event is received.
I have to use a variable bool to hold if the popup is open or not.
A better approach would be: Register to forms FormClosed event, and remove from openPopupList accordingly.
frmCallerID.FormClosed += frmCallerID_FormClosed;
void frmCallerID_FormClosed(object sender, EventArgs e)
{
// remove form from openPopupList
frmCallerID closedPopup = (frmCallerID) sender;
openPopupList.remove(closedPopup);
}

Get data from user control since Webpage

I have a simple question in asp.net.
I want to know if it is possible to get data from controls in my user control directly . I want to do it without using Session variable,Viewstate ...
EDIT: I now use the method of declaring public variables in the UC.
Here is a part of Page_load from my parent page:
this.plan_action = (UCPlan)Page.LoadControl("~/Association/UCPlan.ascx");
PlaceHolder1.Controls.Add(this.plan_action);
if (this.plan_action.Validate == true)
{
CheckBox1.Checked = true;
//String référence = Session["liste_action"].ToString();
for (int i = 0; i < this.plan_action.List1.Count; i++)
{
Label8.Text += this.plan_action.List1[i].Référence + "/";
//Label8.Text += "/";
}
}
but my variable validate stay to false.
Here is the code where I change the value of the validate variable with it declaration:
private bool validate;
public bool Validate
{
get { return validate; }
set { validate = value; }
}
protected void Button2_Click(object sender, EventArgs e)
{
//myCommand.Connection = myConnection;
//Session["liste_action"] = this.List;
this.Validate = true;
//Response.Redirect("../Risques_folder/AjouterRisque.aspx");
}
Thank you for your help,
Quentin
UPDATE due to new information
You need to learn about the sequence of events in ASP.NET.
The Load of the page happens a long time before the Click handler of Button2 in your UserControl... so the Validate property is always going to be set to false.
You have two obvious options (as I see it)...
Keep the creation of the UserControl in your Page_Load (or preferably, move it to your Page_Init, as this is normally the most appropriate place for it). Then place your check for the Validate property in a Page_PreRender.
Or, create an Event in your UserControl, Raise that event on the click of Button2, and handle the event in the Page.
ANOTHER UPDATE
For the 2nd of the two options above, in your UserControl class have the following...
public delegate void ButtonClickedDelegate(object sender, EventArgs e);
public event ButtonClickedDelegate ButtonClicked;
In the Button2_Click method of the UserControl (after setting the this.Validate = true;) call...
ButtonClickedDelegate(sender, e);
In the Page_Init of the Page, put something like...
ctrl1.ButtonClicked += new UCPlan.ButtonClickedDelegate(ctrl1_ButtonClicked);
And then have a new method called something like
void ctrl1_ButtonClicked(object sender, EventArgs e)
{
if (ctrl1.Validate)
{
...
}
}
Remember, as you control the delegate you can pass whatever information you want, including an entire class. So instead of calling the Validate property, create a new instance of the class you want, and pass that as a delegate parameter.
You can find more information on delegates and events on MSDN.
ORIGINAL ANSWER
Unless I've missed something, this is a very simple ASP.NET concept...
You can create properties and/or methods.
For example, as a property...
public string MyProperty
{
get { return "My Property Value"; }
}
Or as a method
public string MyMethod()
{
return "My Method Value";
}
If you're talking about passing the values between the UserControl and the ASP.NET Page that contains it, then in your Page, you can simply call the property or method. If your control was called (for example) myCtrl, then you can something like...
string prop = myCtrl.MyProperty;
string meth = myCtrl.MyMethod();
(On the back of the great comment from AHMED EL-HAROUNY)
If you're talking about passing the values to the client side page, then you can use the same properties / methods directly in the HTML markup. However, in this case, the properties / method can be declared as protected rather than public
For instance, to display the value...
<%=MyProperty%>
Or
<%=MyMethod()%>
Or if you're going to use the value in javascript, something like...
var myProp = "<%=MyProperty%>";
Yes That is possible, But exposing the controls in the UserControl as Public.

How do I get a return value from a Button Click event?

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.

Method executing several times even though it is called once

I am doing a project which includes dynamic controls creation and removal from the WinForm,
So I decided to that part on a small test project.
Test project has two files Form1.cs and NewControls.cs. This program creates additional buttons whenever user clicks an Add button already on the form.And removes the newly created button when it is clicked (self removal button). Also after removal of button other button's Name, Text and their position are changed according to a local variable (controlIndex).
Form1.cs
public partial class Form1 : Form
{
static List<NewControl> newControlsList = new List<NewControl>();
public Form1()
{
InitializeComponent();
}
private void Add_Click(object sender, EventArgs e)
{
newControlsList.Add(new NewControl(newControlsList.Count));
}
public static void RemoveButton(object sender, EventArgs e)
{
NewControl tempNewControl = (NewControl)(sender as Button).Tag;
tempNewControl.RemoveControl();
newControlsList.Remove(tempNewControl);
MessageBox.Show("Removed!");
foreach (NewControl tempcontrol in newControlsList)
{
tempcontrol.controlIndex = newControlsList.IndexOf(tempcontrol);
tempcontrol.PlaceControl();
}
}
}
NewControl.cs
class NewControl
{
public int controlIndex = 0;
Button newButton = new Button();
public NewControl(int index)
{
controlIndex = index;
PlaceControl();
}
public void RemoveControl()
{
newButton.Dispose();
Form1.ActiveForm.Controls.Remove(newButton);
}
public void PlaceControl()
{
newButton.Tag = this;
newButton.Name = "btn" + controlIndex.ToString("D2");
newButton.Text = "btn" + controlIndex.ToString("D2");
newButton.Size = new Size(100, 20);
newButton.Left = controlIndex * 100;
Form1.ActiveForm.Controls.Add(newButton);
newButton.Click += new EventHandler(Form1.RemoveButton);
}
}
Program works nearly as expected. Problem is the MessageBox which I used in form1.cs in RemoveButton() fires many time (as opposed to just one time), which implies whole method being executed several times. Actually I pasted that MessageBox for debugging (sort of).
Since I cannot debug the application as when "Form1.ActiveForm.Controls.Add(newButton);" statement is executed, debugger Throws NullReferenceException, as there is not an active form while debugging.
I know that's like a bonus question but I thought to just put it there. I am a beginner and can't see the way through both the problems. 1st problem is really important for my original project as it will cause problem when many controls are added.
I think it is because you call PlaceControl from Form1.cs AND in the constructor of the NewControl class, Because you say newButton.Click += new EventHandler(Form1.RemoveButton);.
You are adding EventHandlers, so there can be more of them.
So when you call placeControl multiple times, you've got multiple event handlers, i think.
Probably the EventHandler hasn't been removed by RemoveButton. (I've been working in java most recently so my terms might be a little off for C#.) Suggestion: set control visibility to true when you want it and false otherwise rather than adding and removing.
Everytime a button is removed you go over your existing list of controls, and you call "PlaceControl", which attaches yet another handler.
foreach (NewControl tempcontrol in newControlsList)
{
tempcontrol.controlIndex = newControlsList.IndexOf(tempcontrol);
tempcontrol.PlaceControl();
}
Remove the above code block from RemoveButton, and you will see that your dynamically added buttons will each only trigger the event once.
In your RemoveButton event you loop on each button and call again PlaceControl.
The only reason is to reposition the remainder controls.
I think it's better a call to a separate method that do only this work.
public void RepositionControl()
{
newButton.Left = controlIndex * 100;
}
this will prevent to mess with event handlers added more than one time

Categories