I have a class with two properties: weigh and Id. I want to trigger an event when weigh is set. The event to be triggered is a messageBox showing the Id property. The Messagebox is shown, but it doesn't include the Id property.
Here's the entire code:
https://pastebin.com/zpHn48gL
public class MyClass
{
//Custom type Event declaration
public event EventHandler<Mas4TEventArgs> Info;
decimal _weigh;
//properties
public string Id { get; set; }
public decimal Weigh
{
get { return this._weigh; }
set //When this property is changed, invoke Info Event, passing Id property to be shown on messagebox.
{
this._weigh= value;
Info?.Invoke(this, new Mas4TEventArgs(this.Id));
}
}
}
public class Mas4TEventArgs : EventArgs
{
//constructor
public Mas4TEventArgs(string pId) { IdArgu = pId; }
//property IdArgu
public string IdArgu { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MyClass C = new MyClass();
//suscription
C.Info += C_Info;
//Function to be triggered by the event
void C_Info(object sendr, Mas4TEventArgs ev)
{
try
{ //ev.IdArgu doesn't show on the messagebox.
MessageBox.Show("Evento consumido. " + " Id: " + ev.IdArgu);
}
catch (Exception) { }
}
//properties
C.Weigh = Convert.ToDecimal(textBox1.Text);
C.Id = TxtId.Text;
//just to check the two properties have been parsed correctly.This works as intended.
MessageBox.Show("Ingresado: Peso: " + C.Peso.ToString() + " Id: " + C.Id);
}
}
or, if you prefer this:
https://lpaste.net/3710707699130826752
Your code includes the following key lines...
C.Weigh = Convert.ToDecimal(textBox1.Text);
C.Id = TxtId.Text;
Notice that the Id is being set after the Weigh. So at the point where the Weigh value is being set, it will raise an event with whatever was in Id before you set Id on the next line.
Swap the two lines over so that Id is set first, and your code will behave as you are expecting.
Related
using System;
public class NameChangeEventArgs : EventArgs
{
public string Name { get; private set; }
public NameChangeEventArgs(string name)
{
this.Name = name;
}
}
public class Dispatcher
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
OnNameChange(_name);
_name = value;
}
}
public event EventHandler<NameChangeEventArgs> NameChange;
protected virtual void OnNameChange(string name)
{
NameChange?.Invoke(this, new NameChangeEventArgs(name));
}
}
public class Handler
{
public void OnDispatcherNameChange(object Source, NameChangeEventArgs args)
{
Console.WriteLine("Dispatcher's name changed to {0}", args.Name);
}
}
class Program
{
static void Main(string[] args)
{
var dispatcher = new Dispatcher();
var handler = new Handler();
dispatcher.NameChange += handler.OnDispatcherNameChange;
var name = "Sidd";
dispatcher.Name = name;
}
}
The purpose of my code is to change Dispatcher's name to the name passed to it, but whilst this is done, an event is raised to run a method in the Handler class to display the name to which the Dispatcher has been changed to.
The "OnDispatcherNameChange(object Source, NameChangeEventArgs args)" is called to display the message "Dispatcher's name changed to " in my dispatcher's setter.
However it outputs this instead...
Dispatcher's name changed to
Press any key to continue...
What have I done wrong?
I think it's because you're raising the event before changing the property.
The code that receives the event will read the old name and not notice the new one.
Try doing this instead:
set
{
// First change the property
_name = value;
// Then raise the event
OnNameChange(_name);
}
In my Windows form I have 2 text boxes namely, start odometer reading and end odometer reading. My goal is to subtract the "start reading" from the "end reading" and display the difference in the label next to the Name and phone number of the client in the windows form label.
How do I return the value of the method getMilesCharge() and display it on the confirmLabel?
Code for the Car Rental Class
//A class that represents the Rental Agency Class.
namespace Assignment1
{
partial class RentalAgencyClass
{
//instance variables
public string customerName { get; set; }
public string phoneNumber { get; set; }
public double sMiles { get; set; }
public double eMiles { get; set; }
public double noOfDays { get; set; }
private double DAY_CHARGE = 15;
private double MILE_CHARGE = 0.12;
//Constructor class
//sets the value of the starting and ending miles.
//sets the value of the number of days the car was rented for
public RentalAgencyClass(double startMiles, double endMiles, double days)
{
startMiles = sMiles;
endMiles = eMiles;
days = noOfDays;
}
//method to calculate the number of miles driven on the rental
public double getMileCharge()
{
double milesDriven = 0;
milesDriven = eMiles - sMiles;
return milesDriven * MILE_CHARGE;
}
//method to calculate the Day Charges on the rental
public double getDayCharge()
{
return noOfDays * DAY_CHARGE;
}
//Property to display the information on the label
public string GetInfo()
{
return customerName + " | " + phoneNumber + " | " + getDayCharge() +" miles";
}
}
}
Form Designer Class code
namespace Assignment1
{
public partial class RentalAgencyClass : Form
{
RentalAgencyClass aCarRental;
public RentalAgencyClass()
{
InitializeComponent();
}
private void calculateButton_Click(object sender, EventArgs e)
{
try
{
//instantiates object
aCarRental = new RentalAgencyClass();
aCarRental.customerName = nameTextBox.Text;
aCarRental.phoneNumber = phoneTextBox.Text;
//aCarRental. = getDayCharge();
// aCarRental.milesDriven = //store the difference in this variable
//displayLabel.Text = "(student information saved)";
}
catch (Exception err)
{
MessageBox.Show(err.Message, "Error");
}
//Displays information about the Rental
confirmLabel.Text = aCarRental.GetInfo();
}
}
}
By calling aCarRental = new RentalAgencyClass(); within your calculateButton_Click method you are calling the parameterless constructor of your partial class RentalAgencyClass, which means in your case, you are creating a new instance of your form instead of setting your properties. So sMiles and eMiles will stay by their default value 0.
To get your code working you have to do several steps.
At first I recommend you should split your form and your agency class.
So let's say, rename your form class to RentalCalculator. As a next step you have to/can remove the partial from your RentalAgencyClass, because it is not a part of your form class anymore and I assume you did not want to extend your class in another part of your code.
As LarsTech pointed out in the comments. You should now fix your RentalAgencyClass constructor to:
public RentalAgencyClass(double startMiles, double endMiles, double days)
{
this.sMiles = startMiles;
this.eMiles = endMiles;
this.noOfDays = days;
}
and may add the following property to your class
public double milesDriven
{
get
{
return this.eMiles - this.sMiles;
}
}
At least you have to change your event handler:
private void calculateButton_Click(object sender, EventArgs e)
{
try
{
// if not existing you have to create some input textboxes
double startMiles = Convert.ToDouble(startMilesTextBox.Text);
double endMiles = Convert.ToDouble(endMilesTextBox.Text);
double days = Convert.ToDouble(daysTextBox.Text);
// Hint: you are creating a new instance on every button click
// and overwriting your field in your form class.
aCarRental = new RentalAgencyClass(startMiles, endMiles, days);
aCarRental.customerName = nameTextBox.Text;
aCarRental.phoneNumber = phoneTextBox.Text;
// Store the result in local variables
// if you want to do something with them later
double dayCharge = aCarRental.getDayCharge();
double milesCharge = aCarRental.getMilesCharge();
double drivenMiles = aCarRental.milesDriven;
// displayLabel.Text = "(student information saved)";
}
catch (Exception err)
{
MessageBox.Show(err.Message, "Error");
}
//Displays information about the Rental
confirmLabel.Text = aCarRental.GetInfo();
}
Answering your question:
How do I return the value of the method getMilesCharge() and display it on the confirmLabel?
You will have to change the following line in your calculateButton_Click method from:
confirmLabel.Text = aCarRental.GetInfo();
to:
confirmLabel.Text = aCarRental.getMilesCharge().ToString();
Last but not least let me give you a kind advice.
You may take a look at the Microsoft Naming Guidelines.
For example: Properties should be named in PascalCasing.
But this is just my personal opinion.
I have the a class in my application. It has been bound to winform textbox controls. But the textbox which is bound to BookingNo property, always shows zero (0). But i want the textbox keep empty. Is there any way to do it? Here is my code snippet.
public class Booking
{
private int pBookingNo;
private string pCustomerName;
private string pAddress;
public int BookingNo
{
get { return pBookingNo; }
set
{
if (!value.Equals(pBookingNo))
{
pBookingNo = value;
}
}
}
public string CustomerName
{
get { return pCustomerName; }
set
{
if (!value.Equals(pCustomerName))
{
pCustomerName = value;
}
}
}
public Booking() { }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
AddDataBindings();
}
private void AddDataBindings()
{
bsBooking.DataSource = typeof(Booking);
txtBookingNo.DataBindings.Add("Text", bsBooking, "BookingNo", true, DataSourceUpdateMode.OnPropertyChanged, null, "G", GlobalVariables.CurrentCultureInfo);
txtCustomerName.DataBindings.Add("Text", bsBooking, "CustomerName");
}
}
The default value of an Integer is 0, so you have to wrap it into some other object, which supports values other than 0, like
public int? BookingNo { get; set; }
You can use Nullable Type
public int? pBookingNo
{
get;
set;
}
Link : http://msdn.microsoft.com/fr-fr/library/1t3y8s4s(v=vs.80).aspx
You could use custom formatting for the binding by adding a handler to the Format event (http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format.aspx) and return an empty string when the value is zero. But you wouldn't be able to tell whether the value is actually zero or it just hasn't been set already, in which case using the int? approach suggested by #Grumbler85 is better.
what´s about:
textBox1.BindingContextChanged += new EventHandler(BindingContext_Changed);
private void BindingContext_Changed(object sender, EventArgs e)
{
TextBox txtBox = (TextBox)sender;
if (txtBox.Text == "0"){
txtBox.Text = "";
}
}
don´t know if it works, just an idea.
I have an "addEngine" method in a form called Products and i have an "Add New Engine" button.
public partial class Products : Form
{
public void addEngine(short EngineID, string NumberPerUnit, string Manufacturer, string ModelSeriesYear)
{
try
{
productCurrentRow = ((DataRowView) productEngineBindingSource.Current).Row;
int productID = (int) productCurrentRow["ProductID"];
var engines = from engine in productDataset.ProductEngine
where engine.ProductID == productID
select engine;
foreach (var engine in engines)
{
if (engine.EngineID == EngineID)
{
UC.alertError("Record already exists!");
return;
}
}
var newEngineRow = productDataset.ProductEngine.NewProductEngineRow();
newEngineRow.EngineID = EngineID;
newEngineRow.ProductID = productID;
newEngineRow.NumberPerUnit = NumberPerUnit;
newEngineRow.Manufacturer = Manufacturer;
newEngineRow.ModelSeriesYear = ModelSeriesYear;
productDataset.ProductEngine.AddProductEngineRow(newEngineRow);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnAddNewEngine_Clicked(object sender, EventArgs e)
{
EngineFilter eginfilter = new EngineFilter();
eginfilter.ShowDialog();
}
}
When the buttion is clicked there is another form called "EngineFilter", which loads a list Engines. After user selects an item from the list and clicks "Select" buttion on the form, the information of the item is inserted a list (ProductEngine datatable) through the "addEngine" method on the "Product" form. The problem is that i cannot pass the parameters between the two forms(classes)
public partial class EngineFilter : Form
{
public delegate void addEnineDelegate(short EngineID, string NumberPerUnit, string Manufacturer, string ModelSeriesYear);
private void btnSelect_Click(object sender, EventArgs e)
{
Products product = new Products();
DataRow row = ((DataRowView) engineFilterBindingSource.Current).Row;
addEnineDelegate mydelegate = new addEnineDelegate(product.addEngineMethod);
mydelegate((short)row[2], "1", row[0].ToString(), row[1].ToString());
this.Close();
}
}
I've tried to use a delegate but there is a "Object reference not set to an instance of an object" error. Is there anybody can give me a good practice to handle this?
You could pass a reference to the form which contains the addEngine method to the form which you want to call this method from, and that would work, but is not a particularly great solution.
Better would be to have a class which exposes this method, then pass the instance of this class to both forms for them to use. This has better separation of concerns. Really your form should not be responsible for updating the repository, what it should do is collect the user input and pass it to the class responsible for doing the update.
I aggree with Sam Holder, but if you want this code to work as it is right now, I would use events.
Create your event in the EngineFilter
Public event EventHandler<EngineFilterEventArgs> EngineFilterSelected;
Now second step is to create the EngineFilterEventArgs class and put every parameter you want in the eventargs. Now I have just added an ID
Public class EngineFilterEventArgs : EventArgs
{
Public int Id {get; set;}
Public EngineFilterEventArgs(int id)
{
Id = id;
}
}
Third step is to fire the event. Note that 1 is selectedId
If(EngineFilterSelected != null)
EngineFilterSelected(this, new EngineFilterEventArgs(1));
Now make sure that the other form is listening to the event.
private void btnAddNewEngine_Clicked(object sender, EventArgs e)
{
EngineFilter enginFilter = new EngineFilter();
engineFilter.EngineFilterSelected += handler;
engineFilter.ShowDialog();
}
Final step is to add your eventhandler
Protected void handler(object sender, EngineFilterEventArgs e)
{
//TODO Handle your event
Int selectedId = e.Id;
}
Hi coders I have yet another question involving data binding in winforms. I set up a test applications where I have a bindinglist composed of structs called CustomerInfo. I have bound a listbox control to this list and spun a thread to add CustomerInfo items to the bindinglist.
namespace dataBindingSample {
public partial class Form1 : Form {
public BindingList<CustomerInfo> stringList = new BindingList<CustomerInfo>();
public Thread testThread;
public Form1() {
InitializeComponent();
stringList.AllowNew = true;
stringList.RaiseListChangedEvents = true;
listBox1.DataSource = stringList;
testThread = new Thread(new ThreadStart(hh_net_retask_request_func));
testThread.Priority = ThreadPriority.Normal;
}
private void hh_net_retask_request_func() {
int counter = 1;
while (true) {
CustomerInfo cust = new CustomerInfo();
cust.Name = "Customer "+ counter.ToString();
this.Invoke((MethodInvoker)delegate {
stringList.Add(cust);
});
counter++;
Thread.Sleep(1000);
}
}
private void Form1_Load(object sender, EventArgs e) {
testThread.Start();
}
}
public struct CustomerInfo {
public string Name {
set {
name = value;
}
get {
return name;
}
}
private string name;
}
}
What I see in the list box is the name of the struct dataBindingSample.CustomerInfo as opposed to the property of the struct. I was under the impression that non complex binding took the first available property.
Please educate me as to what I am doing wrong.
Thanks,
You'll need to either add an override of ToString() to your CustomerInfo class that returns what you'd like displyed in your list box, or set listBox1.DisplayMemer = "Name" before setting the DataSource.