Updating a WinForm label value from code without buttons - c#

I have a simple question but I could not find a direct answer anywhere
I have a C# program which executes a counter after the user pushes a "start" button. So 1, 2, 3, etc but the increments are performed with casual different time durations, i.e.
1 -> [4 seconds after] 2 -> [7 seconds after] 3 -> etc
and checked in the program every millisecond
I wanted to put an indication on the GUI to let the user know about the number reached
I was thinking to obtain it using a label for the word "Counter:"
// CounterLabel
//
this.CounterLabel.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.CounterLabel.AutoSize = true;
this.CounterLabel.Location = new System.Drawing.Point(1090, 35);
this.CounterLabel.Name = "CounterLabel";
this.CounterLabel.Size = new System.Drawing.Size(58, 17);
this.CounterLabel.TabIndex = 52;
this.CounterLabel.Text = "Counter:";
but then I have two questions:
1) do I need a read-only text box to host the changing number
//
// CounterValue
//
this.CounterValue.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.CounterValue.BackColor = System.Drawing.SystemColors.Control;
this.CounterValue.Location = new System.Drawing.Point(1149, 32);
this.CounterValue.Name = "CounterValue";
this.CounterValue.Size = new System.Drawing.Size(84, 22);
this.CounterValue.TabIndex = 53;
this.CounterValue.ReadOnly = true;
//this.CounterValue.Text += this.GetCounterValue();
or there is a way to have it using only the label?
2) how to perform the control to see if we have to update the UI? I mean, the value to display is checked every msec and I want the interface to be updated every msec as well [without using an "update" button to ask to show the value reached]
Thanks in advance to those who will try to help

1) Yes, you would need read-only text box next to the label.
2) add a method to the form as follows:
void UpdateCounter()
{
if (InvokeRequired)
{
BeginInvoke(new MethodInvoker(UpdateCounter));
return;
}
CounterValue.Text = Counter.ToString();
}
3) call this method everytime the counter is changed.
Or you could use a timer to call the UpdateCounter function.

another option is to use timer control it will automatically changelable value
timer1.star();
private void timer1_Tick(object sender, EventArgs e)
{
//Your code
}

Related

Why are my keyboard events processed after the method completes?

After clicking a button, I am trying to:
Move a cursor to an input box.
Click the box.
Pause for x time.
Type "1".
Pause for x time.
Type "2" in the box.
... The end result should be "12" in the box.
When I run the program without any delays, it works as intended... albeit fast.
When I try to put a delay between the 1 and 2, I see no click or keyboard events get processed and appear on the form until the method finishes. The textbox changes will go from 2, to 4, to 6.
After textbox reaching 6, "12" will appear in the input box. This looks to me like for both times, no events take place until all the code within the method has finished executing.
With my limited knowledge, I am trying to understand:
Why does this happen?
How I can pause in the middle of the method between events without
freezing the entire form?
How can I have each event appear before the next event as opposed to all events happening when the method finishes?
public void DelayTimer(int interval)
{
Task.Delay(interval).Wait();
}
private void typeButton_Click(object sender, EventArgs e)
{
CursorMovementToPoint(PointToScreen(new Point((typeBox.Location.X + (typeBox.Size.Width / 2)), (typeBox.Location.Y + (typeBox.Size.Height / 2))))); // Moves cursor to center of Box
textBox1.Text = "1"; // Indicates event 1 is done
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, Cursor.Position.X, Cursor.Position.Y, 0, 0); // Clicks on box
textBox1.Text = "2"; // Indicates event 2 is done
DelayTimer(1000); // Pauses for 1 sec
textBox1.Text = "3"; // Indicates pause is done
keybd_event(KEY_1, KEY_1_SCAN, 0, 0); // Types "1" into Box
textBox1.Text = "4";
DelayTimer(1000); // Pauses for 1 sec between typing "1" and before typing "2"
textBox1.Text = "5";
keybd_event(KEY_2, KEY_2_SCAN, 0, 0); // Types "2" into Box
textBox1.Text = "6"; // Finished
}
The Button.Click handler is now declared async.
The DelayTimer() method was removed and a delay was placed directly in the typeButton_Click event handler.
This essentially changed the delay from Task.Delay(1000).Wait(); to await Task.Delay(1000);:
private async void typeButton_Click(object sender, EventArgs e)
{
CursorMovementToPoint(PointToScreen(new Point((typeBox.Location.X + (typeBox.Size.Width / 2)), (typeBox.Location.Y + (typeBox.Size.Height / 2)))));
SendM(MOUSEEVENTF.LEFTDOWN | MOUSEEVENTF.LEFTUP);
textBox1.Text = "1";
await Task.Delay(1500);
SendK(ScanCodeShort.KEY_1);
textBox1.Text = "2";
await Task.Delay(1500);
SendK(ScanCodeShort.KEY_2);
textBox1.Text = "3";
}
This resolved the issue: the events were not processed until the method was completed, the Form is not blocked and other events can be processing (now I can also close the Form while a delay is active).
I also added SendInput() to replace my previous methods to call mouse and keyboard events but that was unrelated to the delay issues. Works for me.

How to access Form textboxes inside EventHandler by string in C#?

I am using Visual Studio 2017. There is a form with textboxes. These textboxes need a refresh every 10 seconds. For achieving this I use a Timer event.
public partial class status_window : Form
{
public status_window()
{
InitializeComponent();
shutdown_button.Click += new EventHandler(shutdown_click);
Timer timer = new Timer();
timer.Interval = (1 * 10000); // 10 secs
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
The timer_tick function is member of the status_window class. Inside that eventhandler I can access the textboxes by their name as expected. But how to do that if the textbox "adress" is i variable. Look:
private void timer_Tick(object sender, EventArgs e)
{
Int32 unixtime = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
// for all boxes per exname
for (int i = 0; i < something.Count() ; i++)
{
// try to find textbox 1 -> embty result
Console.WriteLine( this.Controls.Find("nam1_last_event", true) );
Console.WriteLine( this.Controls.Find("nam2_last_event", true) ); // also empty result
// this works and fills the formbox as ecxpected
nam1_last_event.Text = "somevalue";
nam1_event_count.Text = "anothervale";
nam2_last_event.Text = "somemorevalue";
nam2_event_count.Text = "andsoon";
// thats what i want later to use my for loop for those:
// something[i] exuals to nam1,nam2 and so on
this.Controls.Find( String.Format("{0}_last_event", something[i].ToLower()) , true)[0].Text = "somevalue"; // this fails cause array returned by find is empty
this.Controls.Find(String.Format("{0}_last_event", ex_name.ToLower()), true)[0].Text = "anothervale"; // same
}
}
So I stuck here somehow limited by my own knowledge. Most results on Google suggest to use the controls Find Method.
This works for me:
var inPanel = this.Controls.Find("inPanel", true).OfType<TextBox>().FirstOrDefault();
inPanel?.Text = "Found it";
In your code you use equally name nam1_last_event as member of class status_window and name of control. Please check in designer if your control relay has name nam1_last_event.
Function Controls.Find use key which is value of property Name of control.
Create a list or Dictionary variable to save those textboxs, and get it in the timer_Tick.

Get Clicks per Second for a Button

I want to get the clicks per second for a Button and save it in _clicksPerSecond.
I already got how many clicks the user did:
private void button_Click(object sender, RoutedEventArgs e)
{
_klicks++;
}
So, if i click on the button, the counter for a click goes up one, that value will be saved in a Highscore.txt file:
writeHighscore = _klicks + Environment.NewLine;
File.WriteAllText(Path.Combine(savePath, "Highscore.txt"), writeHighscore);
What i need is to count how many _klicks the user did in a second. But I don't know how to get the Time and how to get the value of _klicks just for the second. For now i only get the _klicks the user made all time.
I'm using a WPF-Project for that.
If you want an average, you should save the start time of the period over which you are averaging the clicks, so you can subtract it from the end time (which might be DateTime.Now).
Then you can calculate the average thus:
clicksPerSecond = _klicks / (_startTime - _endTime).TotalSeconds
This works because the DateTime subtraction operator returns a TimeSpan.
You can use a timer that every 1000 milliseconds checks the value of _klicks, saves it in your file and sets it back to 0
void main(){
timer = new Timer();
timer.Interval = 1000; // 1000 miliseconds = 1 second
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
void timer_Tick(object sender, EventArgs e)
{
// Do what you need
var clicks = _klicks;
// method to save clicks to the file
_klicks = 0;
return clicks;
}
This is if you do not need to record when the clicks happened, in that case use the suggestions in the comments.

Change a button color on button click temporarily in C#

This is in Win forms
On button click I want to change the color of the button temporarily say only for 1 second and then the button color should get back to the previous color. I used lambda expression and timer for this.
private void btn_Read_Click(object sender, EventArgs e)
{
System.Windows.Forms.Timer t1 = new System.Windows.Forms.Timer();
t1.Interval = 1000;
t1.Tick += (src, ee) =>
{
btn_Read.BackColor = Color.Transparent; t1.Stop();
};
t1.Start();
btn_Read.BackColor = Color.YellowGreen;
lvwMessages.Items.Clear();
string strcommand = "AT+CMGL=\"ALL\"";
objShortMessageCollection = ReadSMS(strcommand); // Line wher I am reading messages from the port
foreach (ShortMessage msg in objShortMessageCollection)
{
ListViewItem item = new ListViewItem(new string[] { msg.Sender, msg.Message, msg.Sent, msg.Index });
item.Tag = msg;
lvwMessages.Items.Insert(0, item);
}
if (lvwMessages.Items.Count == 0)
{
status_other.Visible = true;
status_other.Text = "No messages";
lbl_total.Text = "Total: 0";
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
timer1.Interval = 2000;
timer1.Tick += (source, ex) => { status_other.Visible = false; timer1.Stop(); };
timer1.Start();
}
else
{
status_other.Visible = false;
chk_selectmsg.Visible = true;
btn_delete.Visible = true;
lbl_total.Text = "Total: " + lvwMessages.Items.Count.ToString(); ;
}
}
Later in this code I am reading the data from serial port, displaying it, etc. The problem is that the button color doesnot change as I click the button. It takes some time and does not give the desired feel I want. Some times doesn't even change the color. What could be the reason?
A simple solution would be using mouse hover event and mouse leave event
Use it this way:
private void btn_Read_MouseHover(object sender, EventArgs e)
{
btn_Read.BackColor = Color.AliceBlue;
}
private void btn_Read_MouseLeave(object sender, EventArgs e)
{
btn_Read.BackColor = Color.AntiqueWhite;
}
This does'nt require any change in your code and definitely will give you the functionality. See if it helps!
you should avoid having work-intensive code on the UI thread
to get the desired effect, sepperate the code for the UI from the code that does the work ...
when the button is clicked, change its appearence and start some background task (threadpool,backgroundworker,etc) that does the work
be aware that you can interact with a control only from the thread it was created on, so to display your data or interact with the UI, you will have to invoke the UI thread (see Control.Invoke(...))
if you have a lot of UI reseting stuff like that, you should think about a timer on the form, to check every let's say 200ms if there is something to be reset/done
you could use a sorted list with tuples (Datetime,delegate) that are executed and removed once the time has come ...
Write the rest of code in thread and fire that thread. this will make your UI responsive and will give you the desired output you want for button. Or use btnedit.Refresh() just after changing color to force button to redraw itself

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.

Categories