Updating a field based on other fields being changed - c#

If I have several fields containing a currency value and a total field, how do I keep the total up-to-date when any value is changed?
I know that I can use events like ActiveChanged or Changed against individual controls but this becomes laborious when you have a lot of fields and also creates a lot of identical methods which seems inefficient to me.
I know that I can also use a button but this requires more input from the user. I'd like it to be somewhat automated so that the total can be observed as values are entered, or at least when switching to the next field.
Currently it's going to be a lot of code like below.
private void field1_Changed(object sender, System.EventArgs e)
{
calculate();
}
private void field2_Changed(object sender, System.EventArgs e)
{
calculate();
}

If exactly same code needs to be executed then you can create only one event handler and point all them to same event handler
Go to form.designer.cs and change event handlers. For very basic example, 2 text box pointing to same event handlers
this.textBox1.TextChanged += new System.EventHandler(this.ValueChanged);
this.textBox2.TextChanged += new System.EventHandler(this.ValueChanged);
In form.cs
private void ValueChanged(object sender, EventArgs e)
{
this.label1.Text = (int.Parse(this.textBox2.Text)
+ int.Parse(this.textBox1.Text))
.ToString();
}

Related

Winform copy button text to textbox using universal method

So this is a fairly straightforward thing, and I am just curious if there is a better way to do it to save lines of code. For class we are making a teletype machine. Basically there is a textbox, and a series of buttons A-Z and 0-9. When you click the button it adds the corresponding letter/number to the textbox. When you click send, it adds the contents of the textbox to a label and resets the textbox. Everything works and it only took a few minutes to build. However there is a mess of redundant lines and I was curious if there is a way to clean up the code with a method.
This is my current code.
private void btn_A_Click(object sender, EventArgs e)
{
box_UserInput.Text = box_UserInput.Text + "A";
}
As you can see, it is very simplistic and straight forward. Click A, and "A" gets added to the textbox. However the Text property of the button is also just "A" and I want to know if there is a way to just copy the text property of that button and add it to the textbox string.
Something like this, except with a universal approach where instead of having to specify btn_A it just inherits which button to copy based on the button clicked. That way I can use the same line of code on every button.
private void btn_A_Click(object sender, EventArgs e)
{
box_UserInput.Text = box_UserInput.Text + btn_A.Text;
}
You can use this which is more universal as the Control class contains the Text property. Also, using the best practice $"".
private void btn_A_Click(object sender, EventArgs e)
{
box_UserInput.Text = $"{box_UserInput.Text}{((Control)sender).Text}";
}
You can also assign the same event to each button. Create an event, say addControlTextOnClick and assign the same event to each button.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void addControlTextOnClick(object sender, EventArgs e)
{
box_UserInput.Text = $"{box_UserInput.Text}{((Control)sender).Text}";
}
}
You can even shorten this more using this C# construct:
private void addControlTextOnClick(object sender, EventArgs e) =>
box_UserInput.Text = $"{box_UserInput.Text}{((Control)sender).Text}";

C# - How to save TextBox and CheckBox in User Settings

I have a Windows Form Application, where the User can input numbers into three different TextBoxes. I want to save these numbers by checking the Checkbox next to it, so when the Application gets closed and re-opened you don't have to put in the numbers again.
I have added the Properties to the User Settings and implemented the Code below, but when I input a number and re-open the Application, nothing is shown and they aren't saved in the user.config file.
Any help is greatly appreciated as I can't find my mistake.
private void MainForm_Load(object sender, EventArgs e)
{
Text = Properties.Settings.Default.title;
chkBox1.Checked = Properties.Settings.Default.checkBox;
chkBox2.Checked = Properties.Settings.Default.checkBox;
chkBox3.Checked = Properties.Settings.Default.checkBox;
txtBox1.Text = Properties.Settings.Default.textBox;
txtBox2.Text = Properties.Settings.Default.textBox;
txtBox3.Text = Properties.Settings.Default.textBox;
this.Location = new System.Drawing.Point(Properties.Settings.Default.PX, Properties.Settings.Default.PY);
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.checkBox = chkBox1.Checked;
Properties.Settings.Default.checkBox = chkBox2.Checked;
Properties.Settings.Default.checkBox = chkBox3.Checked;
Properties.Settings.Default.textBox = txtBox1.Text;
Properties.Settings.Default.textBox = txtBox2.Text;
Properties.Settings.Default.textBox = txtBox3.Text;
Properties.Settings.Default.PX = this.Location.X;
Properties.Settings.Default.PY = this.Location.Y;
Properties.Settings.Default.Save();
}
private void chkBox1_Checked(object sender, EventArgs e)
{
this.Text = txtBox1.Text;
}
private void chkBox2_Checked(object sender, EventArgs e)
{
this.Text = txtBox2.Text;
}
private void chkBox3_Checked(object sender, EventArgs e)
{
this.Text = txtBox3.Text;
}
Why not use databinding to save changes automatically. You don't need to replicate the code on form_load and form_closing events.
The best explanation I have for control data binds is that they provide two way model update between a control properties and object properties.
More Info https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.databindings?view=netcore-3.1
private void Form1_Load(object sender, EventArgs e)
{
chkBox1.DataBindings.Add("Checked", Properties.Settings.Default, "Checked1",true, DataSourceUpdateMode.OnPropertyChanged);
chkBox2.DataBindings.Add("Checked", Properties.Settings.Default, "Checked2",true, DataSourceUpdateMode.OnPropertyChanged);
chkBox3.DataBindings.Add("Checked", Properties.Settings.Default, "Checked3",true, DataSourceUpdateMode.OnPropertyChanged);
//you can others
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//don't forget to call save on form closing
Properties.Settings.Default.Save();
}
The first part of my answer, regarding to the fact that nothing is saved when you close your application, is based on the assumption that when testing, you leave the third textbox empty
Why is nothing saved
First is why you are seeing nothing when opening your application, leading you to believe nothing was saved when closing it.
You are in the part of your code handling what happens when your application is closing, saving all of the textboxes (and checkboxes states) in the same setting
Which leads to the following
txtBox1 contains a
txtbox2 contains nothing (or an empty string if you prefer)
When saving, what is happening with your code is that in a first step, you are putting "a" into your textbox setting.
Then, you are replacing this vlue with the content of the second textbox, which is empty
(repeat for the third textbox)
The you are saving.... An empty value.
If you wish to fix this in a "naive" way, you would need a setting per textbox and checkbox.
Which would lead to code ressembling this in your Closing event handler
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.checkBox1 = chkBox1.Checked;
Properties.Settings.Default.checkBox2 = chkBox2.Checked;
Properties.Settings.Default.checkBox3 = chkBox3.Checked;
Properties.Settings.Default.textBox1 = txtBox1.Text;
Properties.Settings.Default.textBox2 = txtBox2.Text;
Properties.Settings.Default.textBox3 = txtBox3.Text;
Properties.Settings.Default.PX = this.Location.X;
Properties.Settings.Default.PY = this.Location.Y;
Properties.Settings.Default.Save();
}
Why do I say "naive", because as you've surely understood, this approach is not sustainable for a huge number of controls, but this is not the scope of the question, I'll let you research a solution on your own for this particular point.
Why are the checkbox doing nothing to determine what is saved
First, with the events available on Winforms (at least with the .NET Framework 4.5 which I used to reproduce what you had) the only events available to be notified of the checkbox state change are :
CheckedChanged
CheckStateChanged
The first is used on a binary Checkbox (checked or not)
The second on a checkbox with an uncertain state added to both of the other states.
I imagine you used the first of the two (because that is the one used by default by Visual Studio when double clicking on it in the designer).
The first issue here is that it notifiesyou that the state changed not only that it went from unchecked to checked, but the other way around too.
That means if you only want an action to be done when checking, you need to add a.... check (an if block) to skip the cases you're not interest into.
Next is the actual saving.
What you are doing in your code is just copying the textbox values in a property in your class, and that will NOT persist after closing the application.
Now there is two approach you could use to save those values into the settings, the first is to do it as soon as you check the boxes.
What you would need to do then is for each event handler to copy the value of the textbox.... directly into the settings
An example for the first textbox and checkbox :
private void chkBox1_Checked(object sender, EventArgs e)
{
if(chkBox1.Checked)
{
Properties.Settings.Default.checkBox1 = chkBox1.Checked;
}
}
I'm not a huge fan though, and would prefer the second solution => to check in the closing event, before copying the value from the textbox into the settings, if the corresponding checkbox is closed.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (this.chkBox1.Checked)
{
Properties.Settings.Default.textBox = txtBox1.Text;
}
[...]
}
Now that a little better, and should be working as intended.
Please note that this answer is oriented towards correcting the problem whilst using solutions that are the closest possible of your original code.

Function handling across multiple datagridviews with minimal duplicate code

I have 10 datagridviews in a tabcontrol that require the same additional functions like copy/paste to be coded in. The code is fairly long to cover these and I'm trying to minimize the amount of duplicate code used.
My current method is to take the "object sender" from a form item event (such as KeyDown) and pass it to a single function handler method where it is cast to a "DataGridView" object. Then any cell selections and values can be modified using that DataGridView object.
Casting the "object" to a "DataGridView" seems crude. Will this cause any referencing issues down the road? Does this impose any odd limitations? Is there a more "widely used" programming practice to accomplish this task?
Function Handling
private void dgv01_KeyDown(object sender, KeyEventArgs e)
{
dgv_KeyDown_Handler(sender, e);
}
private void dgv02_KeyDown_1(object sender, KeyEventArgs e)
{
dgv_KeyDown_Handler(sender, e);
}
private void dgv03_KeyDown
....
private void dgv_KeyDown_Handler(object sender, KeyEventArgs e)
{
DataGridView dgvHandler = (DataGridView)sender;
// Handles ctrl+c/ctrl+v using dgvHandler to modify cell values
}

Best Practice with Common Event Handling

In a WinForms solution, you have multiple controls of the same type. You need to add an event handler to each of the control and at the current time the event handler will be doing the same thing. You do not expect there to be difference between them down the road any reason.
eg:
ScheduledPatientsGrid.ProcessGridKey += ScheduledPatientsGrid_ProcessGridKey;
RecentPatientsGrid.ProcessGridKey += RecentPatientsGrid_ProcessGridKey;
RecentPatientsGrid.ProcessGridKey += RecentPatientsGrid_ProcessGridKey;
...
private void ScheduledPatientsGrid_ProcessGridKey(object sender, KeyEventArgs e)
{
...
}
private void RecentPatientsGrid_ProcessGridKey(object sender, KeyEventArgs e)
{
...
}
private void PatientsGrid_ProcessGridKey(object sender, KeyEventArgs e)
{
...
}
Now is it better to sharing an single Event Handler between the different events as shown below or use different ones like in the code sample shown above?
ScheduledPatientsGrid.ProcessGridKey += ProcessGridKey;
RecentPatientsGrid.ProcessGridKey += ProcessGridKey;
RecentPatientsGrid.ProcessGridKey += ProcessGridKey;
private void ProcessGridKey(object sender, KeyEventArgs e)
{
...
}
In the following page, Microsoft seems to suggest that sharing is better, however I notice that they have not updated it since .NET 2.0 (ie: Visual Studio 2008)
http://msdn.microsoft.com/en-us/library/4ac48519%28v=vs.90%29.aspx
Is there a Guide that makes a best practices recommendation in this case?
I would absolutely use the same method. What possible benefit is there to having multiple methods which do exactly the same, none of which is named to say what it does?
Personally I abhor the source_EventName convention that Visual Studio spawns. I prefer to give my event handler methods meaningful names which say what they do. Then when you look down the event handler list in the designer, you can see that when a button is clicked, X will happen rather than "the button's click event handler will be called" which is useless.
Alternatively, use lambda expressions to subscribe to the events and call meaningful methods with meaningful parameters. (The sender and args are often useless for event handlers.)
In this case, I usually have them wrap a common method, but I keep their event handlers named per usage. This allows me to easily unit test the method and (usually) reduce the needed parameters, and any errors in the stack trace will be very readable as to which grid the process failed for:
ScheduledPatientsGrid.ProcessGridKey += ScheduledPatientsGrid_ProcessGridKey;
RecentPatientsGrid.ProcessGridKey += RecentPatientsGrid_ProcessGridKey;
RecentPatientsGrid.ProcessGridKey += RecentPatientsGrid_ProcessGridKey;
...
private void ScheduledPatientsGrid_ProcessGridKey(object sender, KeyEventArgs e)
{
ProcessGridKey(e.Key);
}
private void RecentPatientsGrid_ProcessGridKey(object sender, KeyEventArgs e)
{
ProcessGridKey(e.Key);
}
private void PatientsGrid_ProcessGridKey(object sender, KeyEventArgs e)
{
ProcessGridKey(e.Key);
}
private void ProcessGridKey(Key e)
{
...
}
Your mileage may vary depending on what the shared method does, or the parameters passed in. (For example, in my above sample, I duplicate the pulling of the Key from the KeyEventArgs.
I prefer sharing, if the logic gets out of hand you can always just use the single event as a router to the correct method like...
private void ProcessGridKey(object sender, KeyEventArgs e)
{
if (sender is x)
xmethod();
if (sender is y)
ymethod(); //etc
}
I'm aware this syntax doesn't quite make sense as the sender will always be the same object in OP example, but you get the idea.

Updating a textbox using no input controls

I have a windows form application which consists of a bunch of controls, but more specifically, two textBoxes. One of them is read only. The read only textBox value is supposed to be the same as the textBox that the user can type into.
So if the user types "Hello World" into textBox A, the value in textBox B should be automatically updated to "Hello World".
How do I go about doing this? I know I just need to set the text values, I'm just not sure where I place the code to get it done automatically rather than executed when a button is click or something along those lines.
TextChanged event:
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox2.Text = textBox1.Text;
}
It sounds like you want something like:
writableTextBox.TextChanged += delegate {
readonlyTextBox.Text = writableTextBox.Text;
};
In other words, whenever the text in one textbox changes, update the other. This uses the Control.TextChanged event.
If you want textBoxB to be updated as soon as the text of textBoxA is changed (i.e immediately after the user press a key in textBoxA) the event is TextChanged:
this.textBoxA.TextChanged += new System.EventHandler(this.textBoxA_TextChanged);
private void textBoxA_TextChanged(object sender, EventArgs e)
{
textBoxB.Text = textBoxA.Text;
}
If you prefer to update the text in textBoxB only after the user has finished to edit textBoxA, you should use the Leave event:
this.textBoxA.Leave += new System.EventHandler(this.textBoxA_Leave);
private void textBoxA_Leave(object sender, EventArgs e)
{
textBoxB.Text = textBoxA.Text;
}
This should do what you need:
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox2.Text = textBox1.Text;
}
Even shorter (better?) than the event approach is using winform's databinding. Just use this right after the InitializeComponents call:
readonlyTextBox.DataBindings.Add("Text", writableTextBox, "Text");

Categories