I'm writing a C# Windows Form program. The text boxes are for numbers and I don't want to process the number before it is completely entered. I've written the event handlers for processing after the return key is entered. What happens however is when I press the Return key I receive a Windows error sound. I initially thought I needed to handle this in the event handler but I discovered that when the Return key is pressed the keystroke never reaches the event handler. I've looked around for a property for the entire form to allow Return and ESC but cannot find any. Note: Tab key functions correctly.
This is the full source code so far:
namespace Magnetic_Loop_Antenna
{
public partial class MagnetLoopAntenna : Form
{
// Constants
public const double pi = 3.141592653589793238462643383279502884197169399;
public static readonly double rad = pi / 180, DEG = 180 / pi; // PI to RAD
public const double solM = 299792458; // Speed of light in meters/sec
public const double solMm = solM / 1000; // Speed of light in mega meters/sec
public const double solFt = 983571056.43; // Speed of light in feet/sec
public const double ft2mtr = 0.3048; // Convert factor for Feet to Meters
// Default Parameters
public static int units = imperial; // Units to work in, Imperial or Metric
public static double D = 60; // Loop diameter
public static double C = D * pi; // Loop Circumfrence
public static double conductorD = 0.375; // Conductor diameter
public static double RL = 0; // Added loss resistance
public static double xmitP = 25; // RF xmitter power
public static double freq = 14.1; // Frequence
public MagnetLoopAntenna()
{
InitializeComponent();
// Start off filling the form with some default parameter values
tbDsgnFreq.Text = "14.1"; // Antenna Design Frequence
tbLoopDia.Text = "60"; // Primary Loop Diameter
tbConductDia.Text = "0.375"; // Diameter of the conductor
// used to make the loop
tbAddedLoss.Text = "0"; // Any aditional losses not accounted for
tbMaxPower.Text = "25"; // Maximum RF Power to be applied
// This is important to determine
// the minimum variable capacitor
// plate spacing to prevent arcing
}
private void tbDsgnFreq_TextChanged(object sender, EventArgs e)
{
double junk;
junk = 1.0; // Used to put a breakpoint within the event handler
}
private void tbDsgnFreq_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Return)
{
freq = Convert.ToDouble(tbDsgnFreq.Text);
}
}
private void tbLoopDia_TextChanged(object sender, EventArgs e)
{
}
private void tbConductDia_TextChanged(object sender, EventArgs e)
{
}
private void tbAddedLoss_TextChanged(object sender, EventArgs e)
{
}
private void tbMaxPower_TextChanged(object sender, EventArgs e)
{
}
}
}
Anyone have any thoughts?
Bruce
As far as I can tell, when you type the Escape key on a form, the only thing it does is to operate the button set in the form's CancelButton property; you may be able to get around your issue using a button where the Visible property is set to false.
As to the Return key, the Textbox has a property called AcceptsReturn; this is set by default to false, but you can set it to true in the designer.
I am coding in C#, and I have 3 variables that are being updated via my timer every 1000ms.
I want to use this timer to have a FastLine chart that plots the new points every 1000ms obviously.
I have got this working to a certain extent.
It is plotting each tick the timer does, but it just keeps adding to it i only want it to show the previous 20 plots, not the past 2000 if the program has been running that long.
My code below for the chart within my timer1_Tick method:
try
{
chart1.Series[0].Points.AddXY(xaxis++, CPUTemperatureSensor.Value);
chart1.Series[1].Points.AddXY(xaxis, NvdGPUTemperatureSensor.Value);
chart1.Series[2].Points.AddXY(xaxis, ramusedpt);
}
catch
{
}
xaxis is declared previously as an int, no need to show all code as rest is irrelevant
Below code is what was used to solve the problem, posted by the user jstreet
Link to the hread and his comment:
How to move x-axis grids on chart whenever a data is added on the chart
public partial class Form1 : Form
{
Timer timer;
Random random;
int xaxis;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
random = new Random();
timer = new Timer();
timer.Interval = 1000;
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
chart1.Series[0].Points.AddXY(xaxis++, random.Next(1, 7));
if (chart1.Series[0].Points.Count > 10)
{
chart1.Series[0].Points.Remove(chart1.Series[0].Points[0]);
chart1.ChartAreas[0].AxisX.Minimum = chart1.Series[0].Points[0].XValue;
chart1.ChartAreas[0].AxisX.Maximum = xaxis;
}
}
I am trying to fill a rectangle in 3 seconds of real time
I want the increment to be a constant value to look nice and not have acceleration
and I am having trouble understanding what to do
this is my code
// constant
// 1.0f = 100% of rectangle, 3 sec = 3000.0 miliseconds
float addValue = 1.0f/3000.0f;
public override void Update(GameTime gameTime)
{
newGameTime += gameTime.ElapsedGameTime.Milliseconds;
// once the percentage is set to 0 this starts
if ((percentage < 1))
{
// calculate value here in order to time
percentage += addValue;
}
}
I've been trying all kind of crazy math to get it right but i completely lost it. :(
I know I should be using gameTime or newGameTime but I'm lost
I assume thats your update / rendering function.
Let's say, for example, that since the last rendering, 300ms elapsed. That means you'd have to add 100%/3000ms * 300ms = 10% to your rectangle.
-> You're missing the elapsed time in the calculation:
percentage += addValue * gameTime.ElapsedGameTime.Milliseconds;
I may be completely wrong with this answer according to what ccKep just mentioned in his comment.
But just in case it's what you're looking for, I put this together.
The main idea is having a timer event controling the increment. Even if the code I'm submitting isn't appropriate, maybe the idea will apply.
public int percentage;
public int Percentage
{
get { return percentage; }
set
{
percentage = value;
if (Percentage >= 0 && Percentage < 100)
{
progressBar1.Value = value;
}
else
{
Percentage = 0;
timer1.Stop();
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Percentage = 0;
timer1.Interval = 1000;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
double addValue = 100 / 3;
Percentage += (int)addValue;
}
Can you point me in the right direction? I'm trying to get a loop to trigger while the form button is depressed.
//pseudocode
While (button1 is pressed)
value1 += 1
And then of course stop looping when the button is released
To avoid using threads you can add a Timer component on your form/control and simply enable it on mouse down and disable it on mouse up. Then put the code you would normally put inside the loop in the Timer_Tick event. If you want to use System.Timers.Timer you can use the Timer.Elapsed event instead.
Example (using System.Timers.Timer):
using Timer = System.Timers.Timer;
using System.Timers;
using System.Windows.Forms;//WinForms example
private static Timer loopTimer;
private Button formButton;
public YourForm()
{
//loop timer
loopTimer = new Timer();
loopTimer.Interval = 500;/interval in milliseconds
loopTimer.Enabled = false;
loopTimer.Elapsed += loopTimerEvent;
loopTimer.AutoReset = true;
//form button
formButton.MouseDown += mouseDownEvent;
formButton.MouseUp += mouseUpEvent;
}
private static void loopTimerEvent(Object source, ElapsedEventArgs e)
{
//this does whatever you want to happen while clicking on the button
}
private static void mouseDownEvent(object sender, MouseEventArgs e)
{
loopTimer.Enabled = true;
}
private static void mouseUpEvent(object sender, MouseEventArgs e)
{
loopTimer.Enabled = false;
}
You could use a thread to do the counting, and stop the thread when the mouse is released. The following has worked nicely for me:
var b = new Button { Text = "Press me" };
int counter = 0;
Thread countThread = null;
bool stop = false;
b.MouseDown += (s, e) =>
{
stop = false;
counter = 0;
countThread = new Thread(() =>
{
while (!stop)
{
counter++;
Thread.Sleep(100);
}
});
countThread.Start();
};
b.MouseUp += (s, e) =>
{
stop = true;
countThread.Join();
MessageBox.Show(counter.ToString());
};
Of course, if you want the event handlers to be methods rather than lambdas, you will have to turn all the variables into fields.
private void button1_MouseDown(object sender, MouseEventArgs e)
{
timer1.Enabled = true;
timer1.Start();
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
numericUpDown1.Value++;
}
I was inspired by what I read here and decided to write my own button class called a RepeatingButton. On first click it waits for 500ms, then repeats ever 300ms until 2s, then repeats every 100ms (i.e. it uses acceleration).
Here is the code;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
/// <summary>
/// A repeating button class.
/// When the mouse is held down on the button it will first wait for FirstDelay milliseconds,
/// then press the button every LoSpeedWait milliseconds until LoHiChangeTime milliseconds,
/// then press the button every HiSpeedWait milliseconds
/// </summary>
public class RepeatingButton : Button
{
/// <summary>
/// Initializes a new instance of the <see cref="RepeatingButton"/> class.
/// </summary>
public RepeatingButton()
{
internalTimer = new Timer();
internalTimer.Interval = FirstDelay;
internalTimer.Tick += new EventHandler(internalTimer_Tick);
this.MouseDown += new MouseEventHandler(RepeatingButton_MouseDown);
this.MouseUp += new MouseEventHandler(RepeatingButton_MouseUp);
}
/// <summary>
/// The delay before first repeat in milliseconds
/// </summary>
public int FirstDelay = 500;
/// <summary>
/// The delay in milliseconds between repeats before LoHiChangeTime
/// </summary>
public int LoSpeedWait = 300;
/// <summary>
/// The delay in milliseconds between repeats after LoHiChangeTime
/// </summary>
public int HiSpeedWait = 100;
/// <summary>
/// The changeover time between slow repeats and fast repeats in milliseconds
/// </summary>
public int LoHiChangeTime = 2000;
private void RepeatingButton_MouseDown(object sender, MouseEventArgs e)
{
internalTimer.Tag = DateTime.Now;
internalTimer.Start();
}
private void RepeatingButton_MouseUp(object sender, MouseEventArgs e)
{
internalTimer.Stop();
internalTimer.Interval = FirstDelay;
}
private void internalTimer_Tick(object sender, EventArgs e)
{
this.OnClick(e);
TimeSpan elapsed = DateTime.Now - ((DateTime)internalTimer.Tag);
if (elapsed.TotalMilliseconds < LoHiChangeTime)
{
internalTimer.Interval = LoSpeedWait;
}
else
{
internalTimer.Interval = HiSpeedWait;
}
}
private Timer internalTimer;
}
Anywhere you have a button, you can just replace it with a repeating button and it will just have all the new functionality built in.
Enjoy!
Sterren
A recent article from Fabulous Adventures in Coding provides this narrative, which might help answer your question:
A surprising number of people have magical beliefs about how exactly applications respond to user inputs in Windows. I assure you that it is not magic. The way that interactive user interfaces are built in Windows is quite straightforward. When something happens, say, a mouse click on a button, the operating system makes a note of it. At some point, a process asks the operating system "did anything interesting happen recently?" and the operating system says "why yes, someone clicked this thing." The process then does whatever action is appropriate for that. What happens is up to the process; it can choose to ignore the click, handle it in its own special way, or tell the operating system "go ahead and do whatever the default is for that kind of event." All this is typically driven by some of the simplest code you'll ever see:
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
That's it. Somewhere in the heart of every process that has a UI thread is a loop that looks remarkably like this one. One call gets the next message. That message might be at too low a level for you; for example, it might say that a key with a particular keyboard code number was pressed. You might want that translated into "the numlock key was pressed". TranslateMessage does that. There might be some more specific procedure that deals with this message. DispatchMessage passes the message along to the appropriate procedure.
I want to emphasize that this is not magic. It's a while loop. It runs like any other while loop in C that you've ever seen. The loop repeatedly calls three methods, each of which reads or writes a buffer and takes some action before returning. If one of those methods takes a long time to return (typically DispatchMessage is the long-running one of course since it is the one actually doing the work associated with the message) then guess what? The UI doesn't fetch, translate or dispatch notifications from the operating system until such a time as it does return.
Override the OnMouseDown() method in your form and then if the button you want is pressed, that would equal your loop. Example:
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// this is your loop
}
}
It's not a loop in the traditional sense, but should work for what you need.
You will need to handle the MouseDown() event for your form, using the MouseEventArgs argument to figure out which button was pressed.
RepeatButton is perfect for that:
<RepeatButton Delay="1000" Interval="500" HorizontalAlignment="Left" Content="+" Click="IncreaseButton_Click"/>
private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{
value1++;
}
It's been several years since I posted this but someone upvoted it so it popped up in my notifications. Now that I have a lot more experience lol, I thought I'd see if this simple problem is as simple as it sounds, and it was:
public partial class Form1 : Form
{
private bool _isRunning;
public Form1()
{
InitializeComponent();
txtValue.Text = #"0";
btnTest.MouseDown += (sender, args) =>
{
_isRunning = true;
Run();
};
btnTest.MouseUp += (sender, args) => _isRunning = false;
}
private void Run()
{
Task.Run(() =>
{
while (_isRunning)
{
var currentValue = long.Parse(txtValue.Text);
currentValue++;
txtValue.Invoke((MethodInvoker) delegate
{
txtValue.Text = currentValue.ToString();
});
}
});
}
}
Based on Steztric's answer, an extension method with a few bug fixes and different options for the rate of increase.
/// <summary>
/// An extension method to add a repeat click feature to a button. Clicking and holding on a button will cause it
/// to repeatedly fire. This is useful for up-down spinner buttons. Typically the longer the mouse is held, the
/// more quickly the click events are fired. There are different options when it comes to increasing the rate of
/// clicks:
/// 1) Exponential - this is the mode used in the NumericUpDown buttons. The first delay starts off around 650 ms
/// and each successive delay is multiplied by 75% of the current delay.
/// 2) Linear - the delay more slowly reaches the fastest repeat speed. Each successive delay subtracts a fixed
/// amount from the current delay. Decreases in delays occur half a second apart.
/// 3) Two Speed - this delay starts off at a slow speed, and then increases to a faster speed after a specified delay.
/// 4) Three Speed - the repeat speed can increase from slow, to medium, to fastest after a specified delay.
///
/// If repeating is added to a button that already has it, then it will be replaced with the new values.
/// </summary>
public static class RepeatingButtonEx {
private static Hashtable ht = new Hashtable();
private class Data {
private static readonly System.Reflection.MethodInfo methodOnClick = null;
static Data() {
methodOnClick = typeof(Button).GetMethod("OnClick", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
}
public Button Button = null;
private Timer Timer = new Timer();
public double? GradientRate;
public int? LinearGradient = null;
public int FirstDelayMillis;
public int FastestRepeatMillis;
public int[] SwitchesMillis;
public int[] SpeedsMillis;
private DateTime lastEvent = DateTime.MinValue;
private int millisCount = 0;
private int currentSpeed = 0;
private int waitSum = 0;
public Data(Button button, double? gradientRate, int? linearGradient, int firstDelayMillis, int fastestRepeatMillis, int[] switchesMillis, int[] speedsMillis) {
Button = button;
GradientRate = gradientRate;
LinearGradient = linearGradient;
FirstDelayMillis = firstDelayMillis;
FastestRepeatMillis = fastestRepeatMillis;
SwitchesMillis = switchesMillis;
SpeedsMillis = speedsMillis;
Timer.Interval = firstDelayMillis;
Timer.Tick += Timer_Tick;
Button.MouseDown += Button_MouseDown;
Button.MouseUp += Button_MouseUp;
Button.MouseLeave += Button_MouseLeave;
}
void Button_MouseDown(object sender, MouseEventArgs e) {
if (!Button.Enabled)
return;
lastEvent = DateTime.UtcNow;
Timer.Start();
}
void Button_MouseUp(object sender, MouseEventArgs e) {
Reset();
}
void Button_MouseLeave(object sender, EventArgs e) {
Reset();
}
private void Reset() {
Timer.Stop();
Timer.Interval = FirstDelayMillis;
millisCount = 0;
currentSpeed = 0;
waitSum = 0;
}
void Timer_Tick(object sender, EventArgs e) {
if (!Button.Enabled) {
Reset();
return;
}
methodOnClick.Invoke(Button, new Object[] { EventArgs.Empty });
//Button.PerformClick(); // if Button uses SetStyle(Selectable, false); then CanSelect is false, which prevents PerformClick from working.
if (GradientRate.HasValue || LinearGradient.HasValue) {
int millis = Timer.Interval;
if (GradientRate.HasValue)
millis = (int) Math.Round(GradientRate.Value * millis);
else if (LinearGradient.HasValue) {
DateTime now = DateTime.UtcNow;
var ts = now - lastEvent;
int ms = (int) ts.TotalMilliseconds;
millisCount += ms;
// only increase the rate every 500 milliseconds
// otherwise it appears too get to the maximum rate too quickly
if (millisCount >= 500) {
millis -= LinearGradient.Value;
millisCount -= 500;
lastEvent = now;
}
}
if (millis < FastestRepeatMillis)
millis = FastestRepeatMillis;
Timer.Interval = millis;
}
else {
if (currentSpeed < SpeedsMillis.Length) {
TimeSpan elapsed = DateTime.UtcNow - lastEvent;
if (elapsed.TotalMilliseconds >= waitSum) {
waitSum += SwitchesMillis[currentSpeed];
Timer.Interval = SpeedsMillis[currentSpeed];
currentSpeed++;
}
}
}
}
public void Dispose() {
Timer.Stop();
Timer.Dispose();
Button.MouseDown -= Button_MouseDown;
Button.MouseUp -= Button_MouseUp;
Button.MouseLeave -= Button_MouseLeave;
}
}
///<summary>The repeating speed becomes exponentially faster. This is the default behavior of the NumericUpDown control.</summary>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="gradientRate">The new interval is the current interval multiplied by the gradient rate.</param>
public static void AddRepeatingExponential(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 15, double gradientRate = 0.75) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, gradientRate, null, null, null);
}
///<summary>The repeating speed becomes linearily faster.</param>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="linearGradient">If specified, the repeats gradually happen more quickly. The new interval is the current interval minus the linear gradient.</param>
public static void AddRepeatingLinear(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 50, int linearGradient = 25) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, linearGradient, null, null);
}
///<summary>The repeating speed switches from the slow speed to the fastest speed after the specified amount of milliseconds.</summary>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="slowRepeatMillis">The delay in milliseconds between repeats when in the slow repeat state.</param>
///<param name="slowToFastestSwitchMillis">The delay in milliseconds before switching from the slow repeat speed to the fastest repeat speed.</param>
public static void AddRepeatingTwoSpeed(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 100, int slowRepeatMillis = 300, int slowToFastestSwitchMillis = 2000) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, null, new[] { slowRepeatMillis, fastestRepeatMillis }, new [] { slowToFastestSwitchMillis, 0 });
}
///<summary>The repeating speed switches from the slow to medium to fastest at speed switch interval specified.</summary>
///<param name="button">The button to add the behavior.<param>
///<param name="firstDelayMillis">The delay before first repeat in milliseconds.</param>
///<param name="fastestRepeatMillis">The smallest delay allowed. Note: Masharling between the timer and the UI thread has an unavoidable limit of about 10 milliseconds.</param>
///<param name="slowRepeatMillis">The delay in milliseconds between repeats when in the slow repeat state.</param>
///<param name="mediumRepeatMillis">The delay in milliseconds between repeats when in the medium repeat state.</param>
///<param name="speedSwitchMillis">The delay in milliseconds before switching from one speed state to the next speed state.</param>
public static void AddRepeatingThreeSpeed(this Button button, int firstDelayMillis = 500, int fastestRepeatMillis = 75, int slowRepeatMillis = 300, int mediumRepeatMillis = 150, int speedSwitchMillis = 2000) {
AddRepeating(button, firstDelayMillis, fastestRepeatMillis, null, null, new[] { slowRepeatMillis, mediumRepeatMillis, fastestRepeatMillis }, new [] { speedSwitchMillis, speedSwitchMillis, 0 });
}
private static void AddRepeating(this Button button, int firstDelayMillis, int fastestRepeatMillis, double? gradientRate, int? linearGradient, int[] speedsMillis, int[] switchesMillis) {
Data d = (Data) ht[button];
if (d != null)
RemoveRepeating(button);
d = new Data(button, gradientRate, linearGradient, firstDelayMillis, fastestRepeatMillis, switchesMillis, speedsMillis);
ht[button] = d;
button.Disposed += delegate {
RemoveRepeating(button);
};
}
///<summary>Removes the repeating behavior from the button.</summary>
public static void RemoveRepeating(this Button button) {
Data d = (Data) ht[button];
if (d == null)
return;
ht.Remove(button);
d.Dispose();
}
}
you could use the mouseMove Event and check if the mousebutton is held down like:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button==MouseButtons.Left)
{
//your code here
}
}
Similar to Timwi's solution above except using async/await for asynchronous IO and lock for synchronization for some state...
using System;
using System.Windows.Forms;
using System.Threading.Tasks;
namespace Foo {
partial class Form1: Form {
private static readonly object mousePressLock = new object();
private bool mousePressed;
private Task task;
private async Task MouseAction(Action action) {
while (true) {
lock (mousePressLock) {
if (mousePressed)
action();
else
break;
}
await Task.Delay(100).ConfigureAwait(false);
}
}
private void PnlTranslate_Paint(object sender, PaintEventArgs e) {
}
private void Up_MouseUp(object sender, MouseEventArgs e) {
lock (mousePressLock) { mousePressed = false; }
task.Wait();
}
private void Up_MouseDown(object sender, MouseEventArgs e) {
lock (mousePressLock) { mousePressed = true; }
int cnt = 0;
task = MouseAction(() => {
Console.WriteLine($"mouse up action {++cnt}");
});
}
public Form1() {
InitializeComponent();
mousePressed = false;
task = null;
}
}
}
Also, note the ConfigureAwait(false) call. I ran into deadlock without that b.c. tasks were fighting to be on the same thread. It was so annoying.