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);
}
When I press enter, which enables the buttonEditClient_PressEnter function,
the buttonEditClient_ButtonClick function should be called.
private void buttonEditClient_PressEnter(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
//fire buttonEditClient_ButtonClick function
}
}
private async void buttonEditClient_ButtonClick(object sender, ButtonPressedEventArgs e)
{
//buttonEditClient_ButtonClick activated
}
In the Designer:
this.buttonEditClient.ButtonClick += new DevExpress.XtraEditors.Controls.ButtonPressedEventHandler(this.buttonEditClient_ButtonClick);
this.buttonEditClient.KeyDown += new System.Windows.Forms.KeyEventHandler(this.buttonEditClient_PressEnter);
If I try this:
private void buttonEditClient_PressEnter(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
buttonEditClient_ButtonClick(sender, e)
}
}
I get this error:
cannot convert from 'System.Windows.Forms.KeyEventArgs' to 'DevExpress.XtraEditors.Controls.ButtonPressedEventArgs'
How can I activate the buttonEditClient_ButtonClick function?
A click event is inherently different from a keyboard event (e.g., one includes information about the pressed mouse button and cursor position, the other about the pressed key), so you can't pass your KeyEventArgs to the click handler, which expects a ButtonPressedEventArgs.
You have a few simple options here:
Move your code from the button click handler to an extra function, and call that from both your handlers.
Find a way to create a new ButtonPressedEventArgs instance inside the key handler, and then pass that instead of the KeyEventArgs. This would be a very slipshod solution, as you're literally making stuff up (what cursor position are you going to give it?).
The first solution could look something like this:
private void buttonEditClient_PressEnter(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
buttonEditClientSubmit();
}
}
private async void buttonEditClient_ButtonClick(object sender, ButtonPressedEventArgs e)
{
buttonEditClientSubmit();
}
private void buttonEditClientSubmit()
{
// your code...
}
It depends on if you actually need anything from the ButtonPressedEventArgs. If you don't need anything from ButtonPressedEventArgs, you could just have both events call one function.
private void Handle_buttonEditClient()
{
// Do what you want to do when the button is pressed or has the "Enter"
// key pressed on it.
}
private void buttonEditClient_PressEnter(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Handle_buttonEditClient();
}
}
private void buttonEditClient_ButtonClick(object sender, ButtonPressedEventArgs e)
{
Handle_buttonEditClient();
}
Note
I made buttonEditClick_ButtonClick synchronous, but if you leave it async the same thing applies. Just have both events call the same function.
If you need the ButtonPressedEventArgs, then it's like Anas Alweish says. You'll have to create an instance of ButtonPressedEventArgs. I'm not familiar with DevExpress, so I don't know how you'd do that. Maybe something like new ButtonPressedEventArgs(buttonEditClient)?;
DevExpress Docs on ButtonPressedEventArgs
After spending 90 minutes searching for a solution to this simple problem I have to post a question in shame.
I'm working on a WPF project where the user inputs text. I want to check the inputs while the user is typing, display a tool tip and ideally block characters that are not allowed. Basically it's this thread:
How do I validate characters a user types into a WinForms textbox? or this
Is there a best practice way to validate user input?
private void NameTextbox_KeyDown(object sender, KeyEventArgs e)
{
e.???
}
I created this code behind by double clicking in the KeyDown-Property Field in the designer (just mentioning this if I messed up there).
Screenshot of the Property Window
I can not access the e.SupressKeyPress Property. Why?
As of the Properties offered by VS I think that e is of the wrong Type or in the wrong context here.
Intellisense Screenshot
Edit1
private void NameTextbox_KeyDown(object sender, KeyEventArgs e)
{
var strKey = new KeyConverter().ConvertToString(e.Key);
if (!strKey.All(Char.IsLetter))
{
MessageBox.Show("Wrong input");
e.Handled = true;
}
}
Thanks to #rokkerboci I was able to build something that kind of works.
Yet I think it is overly complex. So improvements are still welcome :)
New Error When Creating a Message Box the application hangs without an exception thrown.
You are using WPF, which does not include the WindowsForms specific SupressKeyPress property.
You can do this in WPF by using the KeyDown event, and setting the KeyEventArgs.Handled property to true (it tells the handler, that it doesn't have to do anything with this event.)
private void NameTextbox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
MessageBox.Show("delete pressed");
e.Handled = true;
}
}
EDIT:
I have found a perfect answer to your question:
C#:
char[] invalid = new char[] { 'a', 'b' };
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
foreach (var item in invalid)
{
if (e.Text.Contains(item))
{
e.Handled = true;
return;
}
}
}
private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e)
{
var text = e.DataObject.GetData(typeof(string)).ToString();
foreach (var item in invalid)
{
if (text.Contains(item))
{
e.CancelCommand();
return;
}
}
}
XAML:
<TextBox PreviewTextInput="TextBox_PreviewTextInput" DataObject.Pasting="TextBox_Pasting" />
I am using mask edit TextBox.The textbox always shows 0 (zero). I cannot type any key from the keyboard. I need to delete the zero first then I can type digits. Therefore I am doing extra steps here. Is it possible to type as soon as I type from the keyboard? Any suggestion is welcome.
private void DateDay_GotFocus(object sender, RoutedEventArgs e)
{
if (((TextBox)sender).Text == "Day")
((TextBox)sender).Text = string.Empty;
}
private void DateDay_LostFocus(object sender, RoutedEventArgs e)
{
if (((TextBox)sender).Text == string.Empty)
((TextBox)sender).Text = "Day";
else
CheckForCorrectDateDay((TextBox)sender);
}
I have tried with Focus event but not successful:
You need to select all content in the textbox in GotFocus event. For MaskedTextBox control it handle the selection internally after the focus event fire. So we need to do BeginInvoke to call the SelectAll() afterward.
private void DateDay_GotFocus(object sender, RoutedEventArgs e)
{
this.BeginInvoke((MethodInvoker)delegate() {
((TextBox)sender).SelectAll();
});
}
This way you can start typing directly.
You can't make the text null if null is not allowed.
WPF version:
private void TextBox_GotFocus(object sender, RoutedEventArgs e) {
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate() {
((TextBox)sender).SelectAll();
});
}
Alternate solution for MaskedTextBox using the Enter Event
private void maskedEdit_Enter(object sender, EventArgs e)
{
MaskedTextBox maskedTextBox = (MaskedTextBox)sender;
maskedTextBox.BeginInvoke
(new Action
(() =>
{
maskedTextBox.SelectAll();
}
)
);
}
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
});