UWP AutoSuggestBox - Up/Down arrow key fires SuggestionChosen - c#

When the suggestion list is open, the up/down arrow keys automatically fires the SuggestionChosen event. I am wondering if there is a way to intercept this key press? I have tried to use the KeyDown and KeyUp events to catch these key presses but the SuggestionChosen event occurs before the KeyDown/Up events. This effectively forces the user to choose either the first or the last suggestion on the list. Mouse click or touch selection is fine.
I would just like to ignore the arrow keys while typing in the AutoSuggestBox. Or, not force the user to choose the first or last items with the arrow keys. Is there any way to accomplish this? Thank you
XAML
<AutoSuggestBox Name="EmailSuggestBox"
PlaceholderText="Email"
Text="{Binding Customer.EmailAddress, Mode=TwoWay}"
TextChanged="EmailSuggestBox_TextChanged"
QuerySubmitted="EmailSuggestBox_QuerySubmitted"
SuggestionChosen="EmailSuggestBox_SuggestionChosen"
LostFocus="EmailSuggestBox_LostFocus"
KeyUp="EmailSuggestBox_KeyUp"
KeyDown="EmailSuggestBox_KeyDown" />
Methods (Note: vm.EmailOptions is just a list of email domain suggestions)
private void EmailSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
try
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
if (sender.Text.Contains('#'))
{
var vm = this.DataContext as ProspectInformationEntryViewModel;
var d = sender.Text.Split('#');
var domain = d.LastOrDefault();
List<String> _emailSuggestion = vm.EmailOptions.Where(x => x.StartsWith(domain)).ToList();
sender.ItemsSource = _emailSuggestion;
}
}
}
catch (Exception)
{ }
}
private void EmailSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
try
{
if (args.ChosenSuggestion != null)
{
sender.ItemsSource = null;
}
}
catch (Exception)
{ }
}
private void EmailSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
{
try
{
var domain = args.SelectedItem.ToString();
var temp = sender.Text.Split('#');
var identifier = temp.FirstOrDefault();
sender.Text = identifier + "#" + domain;
sender.ItemsSource = null;
}
catch (Exception)
{ }
}
private void EmailSuggestBox_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Down || e.Key == Windows.System.VirtualKey.Up)
{
e.Handled = true;
}
}
private void EmailSuggestBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Down || e.Key == Windows.System.VirtualKey.Up)
{
e.Handled = true;
}
}

I would just like to ignore the arrow keys while typing in the AutoSuggestBox.
For your requirement, you could use ProcessKeyboardAccelerators event to intercept keydown or keyup press.
private void Autosbox_ProcessKeyboardAccelerators(UIElement sender, ProcessKeyboardAcceleratorEventArgs args)
{
if (args.Key == VirtualKey.Down || args.Key == VirtualKey.Up)
{
args.Handled = true;
}
}

Override the ToString() on your object will do the trick

Related

Textbox on which Keys Delete and backspace Works

I need to have a text box on which events of Delete and Backspace works.Is it possible to have such a text box in C#,or restrict the behavior of text box in such a way. Other keys do not work.
Use TextBox.KeyPress event:
private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back)
{
// your stuff
}
e.Handled = true;
}
For winforms you can do it like this:
protected void myTextBox_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
e.Handled = !IsValidCharacter(e.KeyChar);
}
private bool IsValidCharacter(Keys c)
{
bool isValid = false;
if (c == Keys.Space)
{
isValid = true;
}
return isValid;
}
If you want delete key works ..
private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
Keys k = e.KeyCode
If Not (k = Keys.Back Or k = Keys.Delete)
{
e.Handled = True
}
}

How to lock a textBox in a C# windows app (prevent the user from typing anything) (NOT disabling it or making it unvisible)

:)
I'm making a C# typing program
and I want the user to be unable to type anything when he types a wrong letter,
(I want the typing cursor to freeze at its position)
and when he presses backspace, only then he could resume his typing.
I have done this program in C++ by manipulating the ConsoleScreenCursorCoordinates,
I tried to do the same in C# via manipulating textBox.Location but it didn't work.
In my program, there are 2 textBoxes, the sourceTextBox and the TypingTextBox
there is also a string variable called 'text' which will read from a textFile
via a StreamReader and then I use this text variable to compare each element from it with what the user is typing.
I tired this:
bool madeMistake = false;
Point CurrentTypingPosition;
string whatIsWrittenBeforeTheMistake = "";
private void TypingTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
try
{
if (!madeMistake)
{
if (e.KeyChar == text[typingIndex])
{
typingIndex++;
}
else if (e.KeyChar == backspace)
{
typingIndex--;
}
else
{
CurrentTypingPosition = TypingTextBox.Location;
madeMistake = true;
TypingTextBox.Text += " ";
TypingTextBox.Location = CurrentTypingPosition;
whatIsWrittenBeforeTheMistake = TypingTextBox.Text;
}
}
else
{
if (e.KeyChar == backspace)
madeMistake = false;
else
{
TypingTextBox.Text = whatIsWrittenBeforeTheMistake;
TypingTextBox.Location = CurrentTypingPosition;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Another variation on this is to use the Handled property of the KeyPress event args, so you get something like:
void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (Char.IsControl(e.KeyChar))
{
e.Handled = false;
return;
}
char expectedNext = expected[textBox1.SelectionStart];
if (expectedNext != e.KeyChar)
{
e.Handled = true;
Console.WriteLine("Incorrect input");
}
}
Another, more robust way around this is to create a custom TextBox, inheriting from TextBox itself. You will then be able to handle the KeyDown (PreviewKeyDown) events etc, and determine at each key stroke if something is wrong. If invalid, you can set the KeyEventArgs (e.Handled) to true, and prevent further user input (apart from backspace, which you can check for).
This avoids having to hook into Textbox events, which is desirable if using MVVM. And you can use this method if you require very fine grained control.
Would this work for you?
private void TypingTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
...
if (madeMistake)
TypingTextBox.ReadOnly = true;
...
}
You will have to tweak this to use your StreamReader object in place of the character array I am using here, but this will work, but without requiring them to have to backspace.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
char[] charArr = { 'a', 'b', 'c' }; //spec out what is acceptable here
foreach (char c in charArr)
{
if (e.KeyChar.CompareTo(c) > 0)
{
e.Handled = true;
}
else
{
e.Handled = false;
}
}
}

How do I simulate a Tab key press when Return is pressed in a WPF application?

In a WPF application, i have a window that has a lot of fields.
When the user uses the TAB key after filling each field, windows understands that it moves on to the next. This is pretty know behavior.
Now what I want to to, is make it simulate the TAB key, when in fact the RETURN gets hit.
So in my WPF xaml I added imply KeyDown="userPressEnter"
And in the code behind it:
private void userPressEnter(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
e.Key = Key.Tab // THIS IS NOT WORKING
}
}
Now, obviously this is not working. But what I don't know is, how DO I make this work?
EDIT 1 ==> FOUND A SOLUTION
I found something that helped me out =)
private void userPressEnter(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
MoveFocus(request);
}
}
This way the Focus moves on the the next it can find :)
You can look at a post here:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/c85892ca-08e3-40ca-ae9f-23396df6f3bd
Here's an example:
private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
((TextBox)sender).MoveFocus(request);
}
}
protected override bool ProcessDialogKey(Keys keyData)
{
System.Diagnostics.Debug.WriteLine(keyData.ToString());
switch (keyData)
{
case Keys.Enter:
SendKeys.Send("{TAB}");
break;
}
base.ProcessDialogKey(keyData);
return false;
}
How about make SendKeys Class Working like Winforms.SendKeys
https://michlg.wordpress.com/2013/02/05/wpf-send-keys/
public static class SendKeys
{
public static void Send(Key key)
{
if (Keyboard.PrimaryDevice != null) {
if (Keyboard.PrimaryDevice.ActiveSource != null) {
var e1 = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key) { RoutedEvent = Keyboard.KeyDownEvent };
InputManager.Current.ProcessInput(e1);
}
}
}
}
I think you should use that to simulate TAB :
SendKeys.Send("{TAB}");
Instead of
e.Key = Key.Tab
Sources : http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx
SendKeys.Send or SendKeys.SendWait will not work in a WPF application, so to answer the original question
if (e.Key == Key.Return)
{
KeyEventArgs tabPressEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Tab) { RoutedEvent = Keyboard.KeyDownEvent };
InputManager.Current.ProcessInput(tabPressEventArgs);
}
Use Method SelectNextControl of your Form
I think the best solution is:
var ue = e.OriginalSource as FrameworkElement;
if (e.Key == Key.Return)
{
e.Handled = true;
ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}

User can press and hold only one keyboard button at a time

I have a standard WinForms-application. I want to implement such functionality:
user can press and hold only one keyboard button at a time. If he tried to press a button, while another button pressed, then it gets no result.
PS: this behavior spreads only to a form that I want, not to all forms of my application.
C#, 2.0 - 3.5, VS 2008
I got something similar than Khadaji
private Keys CurrentKey = Keys.None;
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (CurrentKey == Keys.None)
{
CurrentKey = e.KeyData;
// TODO: put your key trigger here
}
else
{
e.SuppressKeyPress = true;
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == CurrentKey)
{
// TODO: put you key end trigger here
CurrentKey = Keys.None;
}
else
{
e.SuppressKeyPress = true;
}
}
I banged this out pretty quickly, so you might have to tinker with it to make it work, but it should get you started.
Set your form's KeyPreview to true. Put the in a KeyDown event and KeyUp Event.
Keys MyKey;
bool KeyIsDown = false;
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (KeyIsDown)
{
e.Handled = true;
}
else
{
MyKey = e.KeyData;
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (KeyIsDown)
{
if (e.KeyData == MyKey)
{
KeyIsDown = false;
}
}
}

How can I catch both single-click and double-click events on WPF FrameworkElement?

I can catch a single-click on a TextBlock like this:
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("you single-clicked");
}
I can catch a double-click on a TextBlock like this:
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (e.ClickCount == 2)
{
MessageBox.Show("you double-clicked");
}
}
}
But how do I catch them both on a single TextBlock and differentiate between the two?
You need to fire the event after the click sequence is over... when is that? I suggest using a timer. The MouseDown event would reset it and increase the click count. When timer interval elapses it makes the call to evaluate the click count.
private System.Timers.Timer ClickTimer;
private int ClickCounter;
public MyView()
{
ClickTimer = new Timer(300);
ClickTimer.Elapsed += new ElapsedEventHandler(EvaluateClicks);
InitializeComponent();
}
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
ClickTimer.Stop();
ClickCounter++;
ClickTimer.Start();
}
private void EvaluateClicks(object source, ElapsedEventArgs e)
{
ClickTimer.Stop();
// Evaluate ClickCounter here
ClickCounter = 0;
}
Cheers!
If you need to detect the difference, I suggest you use a control such as Label that does the work for you:
label.MouseDown += delegate(object sender, MouseEventArgs e)
{
if (e.ClickCount == 1)
{
// single click
}
};
label.MouseDoubleClick += delegate
{
// double click
};
EDIT: My advice was following from documentation on MSDN:
The Control class defines the
PreviewMouseDoubleClick and
MouseDoubleClick events, but not
corresponding single-click events. To
see if the user has clicked the
control once, handle the MouseDown
event (or one of its counterparts) and
check whether the ClickCount property
value is 1.
However, doing so will give you a single click notification even if the user single clicks.
You must use a timer to differentiate between the two. Add a timer to your form in the GUI (easiest that way - it will automatically handle disposing etc...). In my example, the timer is called clickTimer.
private bool mSingleClick;
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (e.ClickCount < 2)
{
mSingleClick = true;
clickTimer.Interval = System.Windows.Forms.SystemInformation.DoubleClickTime;
clickTimer.Start();
}
else if (e.ClickCount == 2)
{
clickTimer.Stop();
mSingleClick = false;
MessageBox.Show("you double-clicked");
}
}
}
private void clickTimer_Tick(object sender, EventArgs e)
{
if (mSingleClick)
{
clickTimer.Stop();
mSingleClick = false;
MessageBox.Show("you single-clicked");
}
}
I did it this Way and it works perfectly
If e.Clicks = 2 Then
doubleClickTimer.Stop()
ElseIf e.Clicks = 1 Then
doubleClickTimer.Enabled = True
doubleClickTimer.Interval = 1000
doubleClickTimer.Start()
End If
Private Sub doubleClickTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles doubleClickTimer.Tick
OpenWebPage("abc")
doubleClickTimer.Stop()
End Sub
You are simply can use MouseDown event and count click number, like this:
if (e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
{
// your code here
}
My suggestion, implemented in a UserControl by simply using a Task:
private int _clickCount = 0;
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
_clickCount = e.ClickCount;
}
protected override async void OnPreviewMouseUp(MouseButtonEventArgs e)
{
if (_clickCount > 1)
{
//apparently a second mouse down event has fired => this must be the second mouse up event
//no need to start another task
//the first mouse up event will be handled after the task below
return;
}
await Task.Delay(500);
if (_clickCount == 1)
{
//single click
}
else
{
//double (or more) click
}
}
The drawback of all these solutions is, of course, that there will be a delay before actually responding to the user's action.
You could do it on MouseUp instead of MouseDown. That way you can ask the ClickCount property for the total number of clicks, and decide what to do from that point.
It's my working solution :)
#region message label click --------------------------------------------------------------------------
private Timer messageLabelClickTimer = null;
private void messageLabel_MouseUp(object sender, MouseButtonEventArgs e)
{
Debug.Print(e.ChangedButton.ToString() + " / Left:" + e.LeftButton.ToString() + " Right:" + e.RightButton.ToString() + " click: " + e.ClickCount.ToString());
// in MouseUp (e.ClickCount == 2) don't work!! Always 1 comes.
// in MouseDown is set e.ClickCount succesfully (but I don't know should I fire one clicked event or wait second click)
if (e.ChangedButton == MouseButton.Left)
{
if (messageLabelClickTimer == null)
{
messageLabelClickTimer = new Timer();
messageLabelClickTimer.Interval = 300;
messageLabelClickTimer.Elapsed += new ElapsedEventHandler(messageLabelClickTimer_Tick);
}
if (! messageLabelClickTimer.Enabled)
{ // Equal: (e.ClickCount == 1)
messageLabelClickTimer.Start();
}
else
{ // Equal: (e.ClickCount == 2)
messageLabelClickTimer.Stop();
var player = new SoundPlayer(ExtraResource.bip_3short); // Double clicked signal
player.Play();
}
}
}
private void messageLabelClickTimer_Tick(object sender, EventArgs e)
{ // single-clicked
messageLabelClickTimer.Stop();
var player = new SoundPlayer(ExtraResource.bip_1short); // Single clicked signal
player.Play();
}
#endregion
My issue was with single/double-clicking rows in a DataGrid in WPF. For some reason the ButtonDown events weren't firing, only the OnMouseLeftButtonUp event was. Anyway, I wanted to handle the single-click differently from the double-click. It looks me a little time (I'm sure the solution isn't perfect, but it appears to work) to distill the problem down until I got it down to the below. I created a Task which calls an Action and that Action's target can be updated by a second click. Hope this helps someone!
private Action _clickAction;
private int _clickCount;
private void Grid_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Button Click Occurred");
_clickCount++;
if (_clickCount == 1)
{
_clickAction = SingleClick;
}
if (_clickCount > 1)
{
_clickAction = DoubleClick;
}
if (_clickCount == 1)
{
Task.Delay(200)
.ContinueWith(t => _clickAction(), TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(t => { _clickCount = 0; });
}
}
private void DoubleGridClick()
{
Debug.WriteLine("Double Click");
}
private void SingleGridClick()
{
Debug.WriteLine("Single Click");
}

Categories

Resources