I am making a game where I need a constant keyboard listener (to navigate through the game). I tried getting the keyboard focus to one place and let it stay there using a seperate thread in a while true loop. This seems to crash my program.
Question:
Is there a method to get my keyboard focused on one element so I can grab my key input from there?
What can I use?:
something that works without throwing exceptions
something I can use in combination with other text input
something that doesn't take hours to compile
something that is easy to build another program (im not super good at c#)
What have I tried?
public MainWindow()
{
InitializeComponent();
Thread keyboardfocus = new Thread(GetFocus);
keyboardfocus.Start();
}
private void GetFocus()
{
while (true)
{
Keyboard.Focus(KeyboardButton);
}
}
private void KeyboardButton_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Z)
{
map.PosUp -= 1;
MainCanvas.Background = Brushes.Aqua;
}
else if (e.Key == Key.S)
{
map.PosUp += 1;
MainCanvas.Background = Brushes.Black;
}
}
Thanks
Add event handler for Window.Loaded and set there a focus to the desired control:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Keyboard.Focus(KeyboardButton);
}
Add event handler for the UIElement.LostKeyboardFocus in your case KeyboardButton and just set the keybord focus again to the KeyboardButton:
private void KeyboardButton_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Keyboard.Focus(KeyboardButton);
}
I did a button, and when u click on the button, it will start the Timer1.Start();
And when u click again on the same button, it will stop the Timer1.Stop();
I tried
private void button1_Click(object sender, EventArgs e)
{
timer1.Start();
else
{
timer1.Stop();
}
}
And the code above is not working, i get always error.
So if i click on the button, timer will run, if i click again on the same button, timer will stop.
You should do it like this:
private void button1_Click(object sender, EventArgs e)
{
ToggleTimer();
}
private void ToggleTimer()
{
!timer1.Enabled ? timer1.Start() : timer1.Stop();
timer1.Enabled = !timer1.Enabled;
}
ToggleTimer() will first check whether the timer has been disabled, and if so, it will either start the timer and mark it as enabled, or stop it and mark it as disabled.
Basically, I have a frequency that refreshes data on screen. When one of these data points goes over a set value, it sets off an error. Upon this error setting off, I want the background colour to change (like a flashing warning).
The problem I have is that I am already using a timer, and when I call a new timer (for the flash) it stops the other timer working, and I'm unaware of how to call in the previous method (being as it uses object sender)
Here is my code:
public void Freq_Change(object sender, SelectionChangedEventArgs e)
{
_timer.Stop();
_timer.Interval = TimeSpan.FromSeconds(Freq.SelectedIndex + 1);
_timer.Start();
_timer.Tick += timer_Tick;
}
and timer_Tick
void timer_Tick(object sender, EventArgs e)
{
//Data generator
//Value pushes to text boxes
if (value is over 100)
{
Warning_Blink
"Oh no, an error"
}
else
{
"All good"
}
Warning_Blink has the new timer in, which then calls warning_Tick
In warning_Tick
private bool _warning = false;
private void warning_Tick(object sender, EventArgs e)
{
if (_warning)
{
ErrorBox.Background = new SolidColorBrush(Colors.Red);
}
else
{
ErrorBox.Background = new SolidColorBrush(Colors.White);
}
_warning = !_warning;
Freq_Change();
}
Here where I call Freq_Change (which doesn't work) I want to be able to go back to the old timer (or better yet never switch between the two) so the data generation can continue.
Can anyone help me with this? I've been scratching my head for hours
Is there a way in C# to wait till the user finished typing in a textbox before taking in values they have typed without hitting enter?
Revised this question a little:
Okay I have a simple calculator that multiplies by 2.
Here is what I want it to do: The user inputs a value like 1000 into a textbox and it automatically displays 2000.
Here is what happens: As soon as the user enters in 1 its multiplies by 2 and outputs 2.
I define "finished typing" now as "user has typed something but has not typed anything after a certain time". Having that as a definition i wrote a little class that derives from TextBox to extend it by a DelayedTextChanged event. I do not ensure that is complete and bug free but it satisfied a small smoke test. Feel free to change and/or use it. I called it MyTextBox cause i could not come up with a better name right now. You may use the DelayedTextChangedTimeout property to change the wait timeout. Default is 10000ms (= 10 seconds).
public class MyTextBox : TextBox
{
private Timer m_delayedTextChangedTimer;
public event EventHandler DelayedTextChanged;
public MyTextBox() : base()
{
this.DelayedTextChangedTimeout = 10 * 1000; // 10 seconds
}
protected override void Dispose(bool disposing)
{
if (m_delayedTextChangedTimer != null)
{
m_delayedTextChangedTimer.Stop();
if (disposing)
m_delayedTextChangedTimer.Dispose();
}
base.Dispose(disposing);
}
public int DelayedTextChangedTimeout { get; set; }
protected virtual void OnDelayedTextChanged(EventArgs e)
{
if (this.DelayedTextChanged != null)
this.DelayedTextChanged(this, e);
}
protected override void OnTextChanged(EventArgs e)
{
this.InitializeDelayedTextChangedEvent();
base.OnTextChanged(e);
}
private void InitializeDelayedTextChangedEvent()
{
if (m_delayedTextChangedTimer != null)
m_delayedTextChangedTimer.Stop();
if (m_delayedTextChangedTimer == null || m_delayedTextChangedTimer.Interval != this.DelayedTextChangedTimeout)
{
m_delayedTextChangedTimer = new Timer();
m_delayedTextChangedTimer.Tick += new EventHandler(HandleDelayedTextChangedTimerTick);
m_delayedTextChangedTimer.Interval = this.DelayedTextChangedTimeout;
}
m_delayedTextChangedTimer.Start();
}
private void HandleDelayedTextChangedTimerTick(object sender, EventArgs e)
{
Timer timer = sender as Timer;
timer.Stop();
this.OnDelayedTextChanged(EventArgs.Empty);
}
}
Another simple solution would be to add a timer to your form, set the Interval property to 250 and then use the timer's tick event as follows:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
Calculate(); // method to calculate value
}
private void txtNumber_TextChanged(object sender, EventArgs e)
{
timer1.Stop();
timer1.Start();
}
If you are using WPF and .NET 4.5 or later there is a new property on the Binding part of a control named "Delay". It defines a timespan after which the source is updated.
<TextBox Text="{Binding Name, Delay=500}" />
This means the source is updated only after 500 milliseconds. As far as I see it it does the update after typing in the TextBox ended. Btw. this property can be usefull in other scenarios as well, eg. ListBox etc.
I faced the same challenge, and here is my simple approach. This works without issues.
public partial class Form2 : Form
{
static int VALIDATION_DELAY = 1500;
System.Threading.Timer timer = null;
public Form2()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox origin = sender as TextBox;
if (!origin.ContainsFocus)
return;
DisposeTimer();
timer = new System.Threading.Timer(TimerElapsed, null, VALIDATION_DELAY, VALIDATION_DELAY);
}
private void TimerElapsed(Object obj)
{
CheckSyntaxAndReport();
DisposeTimer();
}
private void DisposeTimer()
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
private void CheckSyntaxAndReport()
{
this.Invoke(new Action(() =>
{
string s = textBox1.Text.ToUpper(); //Do everything on the UI thread itself
label1.Text = s;
}
));
}
}
You can handle the LostFocus event of the text box which will fire everytime the user finishes typing and navigates away from the text box. Here is the documentation on LostFocus: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.lostfocus.aspx
However, I am not sure what exactly you are trying to do here as the question is not very clear about what "finish" means.
In UWP, I did a delayed check by making a static lastTimeOfTyping and checking the time when the "TextChanged" event happened. This waits till the static lastTimeOfTyping matches when a new "TextChanged" time matches and then executes the desired function.
private const int millisecondsToWait = 500;
private static DateTime s_lastTimeOfTyping;
private void SearchField_OnTextChanged(object sender, TextChangedEventArgs e)
{
var latestTimeOfTyping = DateTime.Now;
var text = ((TextBox)sender).Text;
Task.Run(()=>DelayedCheck(latestTimeOfTyping, text));
s_lastTimeOfTyping = latestTimeOfTyping;
}
private async Task DelayedCheck(DateTime latestTimeOfTyping, string text)
{
await Task.Delay(millisecondsToWait);
if (latestTimeOfTyping.Equals(s_lastTimeOfTyping))
{
// Execute your function here after last text change
// Will need to bring back to the UI if doing UI changes
}
}
As an asynchronous extension method. Adapted from Grecon14's answer.
Note: This is lacking any consideration for cursor position changes, so if the user is moving around with the arrow keys but not actually changing the text it would return true. The question states "finished typing" and I'm not sure if moving the cursor around constitutes actually typing, maybe? As a user I would want it to incorporate this activity. Unfortunately it would need to be more complex than the following for proper interface functionality. See SurfingSanta's answer which has a keydown subscription if you need that.
public static class UIExtensionMethods
{
public static async Task<bool> GetIdle(this TextBox txb)
{
string txt = txb.Text;
await Task.Delay(500);
return txt == txb.Text;
}
}
Usage:
if (await myTextBox.GetIdle()){
// typing has stopped, do stuff
}
I don't know if the onChange() only exists in an older version of c#, but I can't find it!
The following works for detecting when a user either hits the Enter key, or tabs out of the TextBox, but only after changing some text:
//--- this block deals with user editing the textBoxInputFile --- //
private Boolean textChanged = false;
private void textBoxInputFile_TextChanged(object sender, EventArgs e) {
textChanged = true;
}
private void textBoxInputFile_Leave(object sender, EventArgs e) {
if (textChanged) {
fileNameChanged();
}
textChanged = false;
}
private void textBoxInputFile_KeyDown(object sender, KeyEventArgs e) {
if (textChanged & e.KeyCode == Keys.Enter) {
fileNameChanged();
}
textChanged = false;
}
//--- end block --- //
You can use textbox onChange() event. If text is changed in textbox, check if entered value is a number and calculate total value according to the other value.
You want to use handle either the Leave or LostFocus event for the textbox in question. I'm assuming you are using WinForm even though you don't state it in your question.
What if you trigger an event based on a keystroke like tab or return?
A coworker of mine suggested a solution using Rx and event throttling:
var FindDelay = 500;//milliseconds
//textBox is your text box element
Observable.FromEventPattern<EventArgs>(textBox, "TextChanged")
.Select(ea => ((TextBox) ea.Sender).Text)
.DistinctUntilChanged()
.Throttle(TimeSpan.FromMilliseconds(FindDelay))
.Subscribe(text => {
//your handler here
});
Ideally an inheritance solution like esskar’s is the way to go but it doesn’t play well with the designer so to get the re-use I opted for a helper style side-class:
using System;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Threading.Timer;
internal class DelayedText : IDisposable
{
private readonly EventHandler _onTextChangedDelayed;
private readonly TextBox _textBox;
private readonly int _period;
private Timer _timer;
public DelayedText(TextBox textBox, EventHandler onTextChangedDelayed, int period = 250)
{
_textBox = textBox;
_onTextChangedDelayed = onTextChangedDelayed;
_textBox.TextChanged += TextBoxOnTextChanged;
_period = period;
}
public void Dispose()
{
_timer?.Dispose();
_timer = null;
}
private void TextBoxOnTextChanged(object sender, EventArgs e)
{
Dispose();
_timer = new Timer(TimerElapsed, null, _period, Timeout.Infinite);
}
private void TimerElapsed(object state)
{
_onTextChangedDelayed(_textBox, EventArgs.Empty);
}
}
Usage, in the form constructor:
InitializeComponent();
...
new DelayedText(txtEdit, txtEdit_OnTextChangedDelayed);
I haven't kicked it hard, but seems to work for me.
If user is typing fast and you want to delay the calculation until he stopped typing then below code may help:
private void valueInput_TextChanged(object sender, EventArgs e)
{
CalculateAfterStopTyping();
}
Thread delayedCalculationThread;
int delay = 0;
private void CalculateAfterStopTyping()
{
delay += 200;
if (delayedCalculationThread != null && delayedCalculationThread.IsAlive)
return;
delayedCalculationThread = new Thread(() =>
{
while (delay >= 200)
{
delay = delay - 200;
try
{
Thread.Sleep(200);
}
catch (Exception) {}
}
Invoke(new Action(() =>
{
// do your calcualation here...
}));
});
delayedCalculationThread.Start();
}
Most straight forward approach.
*.xaml
<TextBox Name="Textbox1"
TextChanged="Textbox1_TextChanged"/>
*.xaml.cs
using System.Threading.Tasks;
public bool isChanging = false;
async private void Textbox1_TextChanged(object sender,
TextChangedEventArgs e)
{
// entry flag
if (isChanging)
{
return;
}
isChanging = true;
await Task.Delay(500);
// do your stuff here or call a function
// exit flag
isChanging = false;
}
I had the same problem and i think the simplest solution is to use the LostFocus event:
xaml
<TextBox x:Name="YourTextBox" LostFocus="YourTextBox_LostFocus" />
xaml.cs
private void YourTextBox_LostFocus(object sender, RoutedEventArgs e)
{
//Your code here
}
I wanted to commit a textbox both on Return/Tab and on LostFocus, so i have used this convoluted solution, but it works.
public static void TextBoxEditCommit(TextBox tb, Action<TextBox>OnEditCommit)
{
if (OnEditCommit == null)
throw new ArgumentException("OnEditCommit delegate is mandatory.");
//THis delegate fire the OnEditCommit Action
EventHandler _OnEditCommit = delegate(object sender, EventArgs e)
{ OnEditCommit(tb); };
//Edit commit on Enter or Tab
tb.KeyDown += delegate (object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab)
{
//Temporary remove lostfocus event for avoid double commits
tb.LostFocus -= _OnEditCommit;
OnEditCommit(tb);
tb.LostFocus += _OnEditCommit;
}
};
//Edit commit on LostFocus
tb.LostFocus += _OnEditCommit;
}
You can use this event generator with this simple code:
//Check for valid content
UIUtil.TextBoxEditCommit(tbRuleName, (tb) => {
//Your code here, tb.text is the value collected
});
When I use System.Windows.Forms.Timer class and finish using it then I can't disable it.. it ticks even if I set its property Enabled to false. What is wrong with the code? here is an example:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
MessageBox.Show("Hello");
counter++;
if (counter == 10){
timer1.Enabled = false;
}
}
This is a subtle bug that's induced by the MessageBox.Show() call. MessageBox pumps a message loop to keep the UI alive. Which allows the Tick event handler to run again, even though it is already active from the previous tick. The counter variable doesn't get incremented until you click the OK button. As a result, the screen fills with message boxes and that won't stop until you click the OK button ten times.
You need to increment the counter before showing the message box. Fix:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
counter++;
if (counter > 10) timer1.Enabled = false;
else MessageBox.Show("Hello");
}
This kind of problem is also the reason that DoEvents() got such a bad reputation. It is pretty difficult to write code that can properly deal with the re-entrancy induced by the message loop. You need to keep boolean flags around that indicate that code is already active. Which is another way to solve your problem:
int counter = 0;
bool showingBox = false;
private void timer1_Tick(object sender, EventArgs e) {
if (showingBox) return;
showingBox = true;
try {
MessageBox.Show("Hello");
counter++;
if (counter == 10) timer1.Enabled = false;
}
finally {
showingBox = false;
}
}
You now get only one message box at a time, probably what you are really looking for.
I should mention that this re-entrancy problem is pretty specific to timers. A dialog takes counter-measures to avoid re-entrancy problems, it disables all the windows in the application. That ensures that the user cannot do things like closing the main window or clicking a button that brings up the dialog again. Both rather disastrous mishaps. That takes care of most of the 'unexpected' Windows notifications, basically any of the messages that are generated by the user. The edge case is a timer (WM_TIMER is not disabled) whose event handler has a UI effect.
It is because the MessageBox.Show blocks until the user presses OK.
The code below the MessageBox will not execute until after 10 OK buttons are pressed.
But the timer continues to fire even if the execution is blocked.
Try this code
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
counter++;
if (counter == 10){
timer1.Enabled = false;
}
MessageBox.Show("Hello");
}
(just moved the MessageBox)
What about timer1.Stop()? I am not too familiar with this class, but looked it up quickly: Timer Class
try this:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
MessageBox.Show("Hello");
counter++;
if (counter == 10){
timer1.Stop();
timer1.Dispose();
timer1 = null;
}
}
It is also working for me. I placed the timer in the form, on the button click, I am calling timer1.start() and I put the following code in the tick event and its working.
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
i++;
this.Text = i.ToString();
if (i == 10)
{
timer1.Enabled = false;
}
}
You need to call Stop.
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
i++;
if (i == 10)
{
timer1.Stop();
}
}
I'm pretty sure the MessageBox is the culprit here. Maybe if you use a short execution interval for the timer handler then this could potentially cause your code to function undesirably, if executions are overlapping.
The problem would be, in this case, that the handler executes and displays the MessageBox which in turn halts execution of the current scope until the the prompt is acknowledged by the user, meanwhile the handler has started again, showing another prompt, and another, and so on. At this point, we have multiple MessageBoxes waiting for input, yet counter hasn't even been incremented once. When we click 'OK' on the prompt, counter increments as desired, but at this point has a value of 1, rather than a value representing the number of prompts shown. This means yet another prompt will be displayed if more time elapses, until the user clicks 'OK' on at least 10 prompts.
You could try inhibiting execution if the process is already under way in order to prevent concurrent runs:
private readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
int counter = 0;
private void timer1_Tick(object sender, EventArgs e)
{
if (Locker.TryEnterWriteLock(0))
{
try
{
MessageBox.Show("Hello");
Counter++;
if (Counter == 10)
{
Timer.Enabled = false;
}
}
catch { }
finally
{
Locker.ExitWriteLock();
}
}
}