xamarin.android - OnKeyLongPress does not trigger - c#

I want to use OnKeyLongPress on volume buttons, to override system volume control.
I am trying to use OnKeyLongPress event listener, but it's not working.
here is my code:
public override bool OnKeyLongPress(Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.VolumeDown)
{
return true;
}
if (keyCode == Keycode.VolumeUp )
{
return true;
}
return base.OnKeyLongPress(keyCode, e);
}

You are almost there. You need to detect the same key event in the onKeyPress handler and start tracking it so that the long press can work. Here's the code you need:
public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.VolumeDown)
{
return true;
}
if (keyCode == Keycode.VolumeUp )
{
return true;
}
return base.OnKeyDown(keyCode, e);
}

Related

xamarin.forms - event listener for volume up and down keys

I am writing an xamarin.forms app and I want to open 'special settings' page when press both volume up and down buttons.
Is there any way to do that in xamarin.forms?
For now, I have done an event listener, which works correctly. Now last thing i need to do is to pass the binding context to Settings Page.
List<Keycode> _pressedKeys = new List<Keycode>();
public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
{
if(keyCode == Keycode.VolumeDown)
{
_pressedKeys.Add(keyCode);
CheckBoth();
return true;
}
if (keyCode == Keycode.VolumeUp)
{
_pressedKeys.Add(keyCode);
CheckBoth();
return true;
}
return base.OnKeyDown(keyCode, e);
}
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.VolumeDown)
{
_pressedKeys.Remove(keyCode);
return true;
}
if (keyCode == Keycode.VolumeUp)
{
_pressedKeys.Remove(keyCode);
return true;
}
return base.OnKeyUp(keyCode, e);
}
public void ChangePage()
{
((App)App.Current).ChangeScreen(new SettingsPage());
}
public void CheckBoth()
{
try
{
if (_pressedKeys.Contains(Keycode.VolumeDown) &&
_pressedKeys.Contains(Keycode.VolumeUp))
{
Console.WriteLine("It works!");
((App)App.Current).ChangeScreen(new SettingsPage());
_pressedKeys.Clear();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
Solved. I added following line:
var vm= App.Current.MainPage.BindingContext as MainViewModel;
I think you might have to go platform specific with this implementation, but here is the Android implementation for handling volume button presses:
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
if (keyCode == Keycode.VolumeDown)
{
//Dostuff();
return true;
}
if (keyCode == Keycode.VolumeUp)
{
//Dostuff();
return true;
}
return base.OnKeyUp(keyCode, e);
}

How to keep keyboard on Xamarin.Forms?

Entry.unfocus/Entry.completed hides keyboard, how to cancel it?
I have a page with some entries and when I press keyboard enter key, I want the keyboard not hides. How to do that with PCL project (Android e iOS)?
Just to point out another solution for Android. In case you want to keep always visible the keyboard for a specific Editor Renderer, you need to override the following methods in the MainActivity class:
private bool _lieAboutCurrentFocus;
public override bool DispatchTouchEvent(MotionEvent ev)
{
var focused = CurrentFocus;
bool customEntryRendererFocused = focused != null && focused.Parent is YourCustomEditorRenderer;
_lieAboutCurrentFocus = customEntryRendererFocused;
var result = base.DispatchTouchEvent(ev);
_lieAboutCurrentFocus = false;
return result;
}
public override Android.Views.View CurrentFocus
{
get
{
if (_lieAboutCurrentFocus)
{
return null;
}
return base.CurrentFocus;
}
}
You can find a more detail explanation here
Hope this helps.
Regards
If you want to do that from the PCL there's a nice and easy way to navigate through your entries and keep them focused one after the other (If this is what you're looking for, and not just keep keyboard open)
Let's say you have around 5 entries in your page, and you want to cycle through them when user presses the done or enter key.
CurrentPage.FindByName<Entry>("FirstEntry").Completed += (o, args) =>
{
CurrentPage.FindByName<Entry>("SecondEntry").Focus();
};
CurrentPage.FindByName<Entry>("SecondEntry").Completed += (o, args) =>
{
CurrentPage.FindByName<Entry>("ThirdEntry").Focus();
};
CurrentPage.FindByName<Entry>("ThirdEntry").Completed += (o, args) =>
{
CurrentPage.FindByName<Entry>("ForthEntry").Focus();
};
CurrentPage.FindByName<Entry>("ForthEntry").Completed += (o, args) =>
{
CurrentPage.FindByName<Entry>("FifthEntry").Focus();
};
CurrentPage.FindByName<Entry>("FifthEntry").Completed += (o, args) =>
{
//Keep going or execute your command, you got the drill..
};
You can add this to your ViewIsAppearing or Init method.
Recently i did something similar. I want to keep keyboard always open in a page and not to hide when a button clicked. To accomplish this, i followed different ways both on iOS and Android.
iOS
In iOS, i created a custom editor renderer
public class CustomEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
var element = this.Element as CustomEditor;
Control.InputAccessoryView = null;
Control.ShouldEndEditing += DisableHidingKeyboard;
MessagingCenter.Subscribe<ReportEventDetailPage>(this, "FocusKeyboardStatus", (sender) =>
{
if (Control != null)
{
Control.ShouldEndEditing += EnableHidingKeyboard;
}
MessagingCenter.Unsubscribe<ReportEventDetailPage>(this, "FocusKeyboardStatus");
});
}
private bool DisableHidingKeyboard(UITextView textView)
{
return false;
}
private bool EnableHidingKeyboard(UITextView textView)
{
return true;
}
}
In this piece of code:
Control.ShouldEndEditing += DisableHidingKeyboard; makes keyboard always opened after focusing custom editor. However, the keyboard does not hide when changing current page to another page. To solve this problem i used MessagingCenter and when dissapering of the current page i send a message to hide keyboard.
Android
For Android, i created a keyboard helper interface and implemented it.
Here is my interface:
public interface IKeyboardHelper
{
void ShowKeyboard();
void HideKeyboard();
}
Keyboard Helper class for Android:
public class KeyboardHelper : IKeyboardHelper
{
public void ShowKeyboard()
{
var context = Forms.Context;
var inputMethodManager = context.GetSystemService(Context.InputMethodService) as InputMethodManager;
if (inputMethodManager != null && context is Activity)
{
var activity = context as Activity;
var token = activity.CurrentFocus?.WindowToken;
inputMethodManager.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
}
}
public void HideKeyboard()
{
var context = Forms.Context;
var inputMethodManager = context.GetSystemService(Context.InputMethodService) as InputMethodManager;
if (inputMethodManager != null && context is Activity)
{
var activity = context as Activity;
var token = activity.CurrentFocus?.WindowToken;
inputMethodManager.HideSoftInputFromWindow(token, HideSoftInputFlags.None);
activity.Window.DecorView.ClearFocus();
}
}
in Constructor of the current page:
else if (Device.OS == TargetPlatform.Android)
{
MessagingCenter.Send(this, "AndroidFocusEditor");
}
and OnAppearing method of the current page:
protected override void OnAppearing()
{
base.OnAppearing();
if (Device.OS == TargetPlatform.Android)
{
DependencyService.Get<IKeyboardHelper>().ShowKeyboard();
//EventEditor.Focus();
MessagingCenter.Subscribe<ReportEventDetailPage>(this, "AndroidFocusEditor", (sender) => {
Device.BeginInvokeOnMainThread(async () => {
await Task.Run(() => Task.Delay(1));
EventEditor.Focus();
MessagingCenter.Unsubscribe<ReportEventDetailPage>(this, "AndroidFocusEditor");
});
});
}
else if (Device.OS == TargetPlatform.iOS)
{
EventEditor.Focus();
}
}
One last thing: if user clicks another button on the page, keyboard is hiding. To prevent this i followed this link and it really helped me a lot
Keep Keyboard Open For Android
In case you have a custom Keyboard, you can implement a "show" and a "hide" method on android renderer.
Then on your page, show keyboard on your custom control without hiding it. You can hide it when changing page, by overriding OnBackButtonPressed.
In OnBackButtonPressed, send a message using MessagingCenter. Then subscribe to it on your custom control constructor.
Declare an EventHandler that you invoke in the callback method.
Subscribe to this event on your android custom entry renderer and hide the keyboard there.
I had a similar problem and handled it like below:
using System.Runtime.CompilerServices;
using Xamarin.Forms;
public class CustomEntry: Entry
{
public static readonly BindableProperty KeyboardAliveProperty =
BindableProperty.Create(nameof(KeyboardAliveType), typeof(KeyboardAliveType),
typeof(CustomEntry), KeyboardAliveType.Default);
public KeyboardAliveType KeyboardAliveType
{
get { return (KeyboardAliveType)GetValue(KeyboardAliveProperty); }
set { SetValue( KeyboardAliveProperty, value);}
}
}
public enum KeyboardAliveType
{
Default =0,
OnCompleted = 1,
OnButtonClicked = 2,
OnCompletedAndButtonClicked = 3
}
Renderer for Android:
using System;
using Android.Content;
using Android.OS;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Entry = Xamarin.Forms.Entry;
[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
/// <summary>
/// Allow and support changes to Border styling and Keyboard with Custom Entry.
/// </summary>
public class CustomEntryRenderer: EntryRenderer, TextView.IOnEditorActionListener
{
private ImeAction _currentInputImeFlag;
public CustomEntryRenderer(Context context) : base(context)
{
//do nothiing
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement!=null)
{
}
}
bool TextView.IOnEditorActionListener.OnEditorAction(TextView v, ImeAction actionId, KeyEvent e)
{
// Fire Completed and dismiss keyboard for hardware / physical keyboards
if (actionId == ImeAction.Done || actionId == _currentInputImeFlag ||
(actionId == ImeAction.ImeNull && e.KeyCode == Keycode.Enter && e.Action == KeyEventActions.Up))
{
global::Android.Views.View nextFocus = null;
if (_currentInputImeFlag == ImeAction.Next)
{
nextFocus = FocusSearch(v, FocusSearchDirection.Forward);
}
if (nextFocus != null)
{
nextFocus.RequestFocus();
if (!nextFocus.OnCheckIsTextEditor())
{
if (Element is CustomEntry cE)
{
if (cE.KeyboardAliveType != KeyboardAliveType.OnCompleted &&
cE.KeyboardAliveType != KeyboardAliveType.OnCompletedAndButtonClicked)
{
v.HideKeyboard();
}
}
}
}
else
{
EditText.ClearFocus();
if (Element is CustomEntry cE)
{
if (cE.KeyboardAliveType != KeyboardAliveType.OnCompleted &&
cE.KeyboardAliveType != KeyboardAliveType.OnCompletedAndButtonClicked)
{
v.HideKeyboard();
}
}
}
((IEntryController)Element).SendCompleted();
}
return true;
}
}
internal static class CustomEntryRendererExtensions
{
internal static void HideKeyboard(this Android.Views.View inputView, bool overrideValidation = false)
{
if (inputView == null)
throw new ArgumentNullException(nameof(inputView) + " must be set before the keyboard can be hidden.");
using (var inputMethodManager = (InputMethodManager)inputView.Context?.GetSystemService(Context.InputMethodService))
{
if (!overrideValidation && !(inputView is EditText || inputView is TextView || inputView is SearchView))
throw new ArgumentException("inputView should be of type EditText, SearchView, or TextView");
IBinder windowToken = inputView.WindowToken;
if (windowToken != null && inputMethodManager != null)
inputMethodManager.HideSoftInputFromWindow(windowToken, HideSoftInputFlags.None);
}
}
}
In MainActivity.cs
private bool _lieAboutCurrentFocus;
public override bool DispatchTouchEvent(MotionEvent ev)
{
var focused = CurrentFocus;
if (focused?.Parent is CustomEntryRenderer cer)
{
if (cer.Element is CustomEntry cEntry)
{
if (cEntry.KeyboardAliveType == KeyboardAliveType.OnButtonClicked ||
cEntry.KeyboardAliveType == KeyboardAliveType.OnCompletedAndButtonClicked)
{
_lieAboutCurrentFocus = true;
}
}
}
var result = base.DispatchTouchEvent(ev);
_lieAboutCurrentFocus = false;
return result;
}
public override Android.Views.View CurrentFocus
{
get
{
if (_lieAboutCurrentFocus)
{
return null;
}
return base.CurrentFocus;
}
}
Renderer for UWP:
using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.System;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
public class CustomEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
// Remove the EventHandler set for KeyUp, and add my custom EventHandler.
// Had to do it this way (using WindowsRuntimeMarshal) because the Delegate that
// I want to remove from the KeyUp event is marked private in a different assembly, so no way to access it directly.
// This way I can customize how the keyboard behaves when the Enter key is pressed.
/*Done the best I can for UWP.*/
var keyUpRuntimeEvent = this.Control.GetType().GetRuntimeEvent("KeyUp");
Action<EventRegistrationToken> removeEventHandlerAction =
(Action<EventRegistrationToken>)Delegate.CreateDelegate(typeof(Action<EventRegistrationToken>),
this.Control,
keyUpRuntimeEvent.RemoveMethod);
WindowsRuntimeMarshal.RemoveAllEventHandlers(removeEventHandlerAction);
this.Control.KeyUp += TextBoxOnKeyUp;
this.Control.PreventKeyboardDisplayOnProgrammaticFocus = false;
// Just to make sure that keyboard is up when the Entry is focused.
Control.GotFocus += (sender, args) =>
{
AttemptToForceKeyboardToShow(Control);
};
Control.TextChanged += (sender, args) =>
{
if (Control.FocusState != FocusState.Unfocused)
{
AttemptToForceKeyboardToShow(Control);
}
};
}
}
protected override void Dispose(bool disposing)
{
if (disposing && Control != null)
{
Control.KeyUp -= TextBoxOnKeyUp;
}
base.Dispose(disposing);
}
private void TextBoxOnKeyUp(object sender, KeyRoutedEventArgs args)
{
if (args?.Key != VirtualKey.Enter)
{
return;
}
if (Element.ReturnType == ReturnType.Next)
{
FocusManager.TryMoveFocus(FocusNavigationDirection.Next);
}
else
{
/*Done the best I can for UWP.*/
if (Element is CustomEntry cE)
{
if (cE.KeyboardAliveType != KeyboardAliveType.OnCompleted &&
cE.KeyboardAliveType != KeyboardAliveType.OnCompletedAndButtonClicked)
{
//Hide the soft keyboard; this matches the behavior of Forms on Android/iOS
Windows.UI.ViewManagement.InputPane.GetForCurrentView().TryHide();
}
}
}
((IEntryController)Element).SendCompleted();
}
private void AttemptToForceKeyboardToShow(FormsTextBox control)
{
try
{
var inputPane = InputPane.GetForUIContext(control.UIContext);
var keyboardShowSuccess = inputPane?.TryShow();
if (keyboardShowSuccess == null || !keyboardShowSuccess.Value)
{
Console.WriteLine("Attempt to force Keyboard to show failed on Windows.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
See here.

Disable spacekey textbox in c# wpf

I have textbox that need to allow only numbers, delete and backsapce. This code works for this, but its allowing space key even though I disabled it. I'm not sure what is missing here.
public class NumberTextBox : TextBox
{
/// <summary>
/// Allows only numbers, delete, backspace keys
/// </summary>
public NumberTextBox()
{
KeyDown += new KeyEventHandler(OnKeyDown);
}
private bool IsNumberKey(Key inKey)
{
if (inKey < Key.D0 || inKey > Key.D9)
{
if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
{
return false;
}
}
return true;
}
private bool IsActionKey(Key inKey)
{
return inKey == Key.Delete || inKey == Key.Back;
}
private bool IsSpaceKey(Key inKey)
{
if (inKey == Key.Space)
{
return true;
}
return false;
}
protected void OnKeyDown(object sender, KeyEventArgs e)
{
e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key) && IsSpaceKey(e.Key);
}
}
From the microsoft website:
public class NumericTextBox : TextBox
{
bool allowSpace = false;
// Restricts the entry of characters to digits (including hex), the negative sign,
// the decimal point, and editing keystrokes (backspace).
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
string decimalSeparator = numberFormatInfo.NumberDecimalSeparator;
string groupSeparator = numberFormatInfo.NumberGroupSeparator;
string negativeSign = numberFormatInfo.NegativeSign;
string keyInput = e.KeyChar.ToString();
if (Char.IsDigit(e.KeyChar))
{
// Digits are OK
}
else if (keyInput.Equals(decimalSeparator) || keyInput.Equals(groupSeparator) ||
keyInput.Equals(negativeSign))
{
// Decimal separator is OK
}
else if (e.KeyChar == '\b')
{
// Backspace key is OK
}
// else if ((ModifierKeys & (Keys.Control | Keys.Alt)) != 0)
// {
// // Let the edit control handle control and alt key combinations
// }
else if (this.allowSpace && e.KeyChar == ' ')
{
}
else
{
// Swallow this invalid key and beep
e.Handled = true;
// MessageBeep();
}
}
public int IntValue
{
get
{
return Int32.Parse(this.Text);
}
}
public decimal DecimalValue
{
get
{
return Decimal.Parse(this.Text);
}
}
public bool AllowSpace
{
set
{
this.allowSpace = value;
}
get
{
return this.allowSpace;
}
}
}

Override KeyDown from another class C#

I have a form that creates a class. This class processes events that are fired on the form. The problem is I am trying to use the KeyDown event, but it isn't working because there are buttons on the form and they are capturing the KeyDown. I found the solution on another post was to override the ProcessCmdKey. The problem is I don't know how to override a method from inside another class. Can anyone tell me how I can capture all KeyDown events from inside my other class?
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Left)
{
MoveLeft(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else if (keyData == Keys.Right)
{
MoveRight(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else if (keyData == Keys.Up)
{
MoveUp(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else if (keyData == Keys.Down)
{
MoveDown(); DrawGame(); DoWhatever();
return true; //for the active control to see the keypress, return false
}
else
return base.ProcessCmdKey(ref msg, keyData);
}
The easiest way to do this would be to expose the KeyDown from Button on the containing form.
class MyForm : Form {
Button m_button;
public event KeyEventHandler ButtonKeyDown;
public MyForm() {
m_button = ...;
m_button.KeyDown += delegate (object, e) {
KeyEventHandler saved = ButtonKeyDown;
if (saved != null) {
saved(object, e);
}
};
}
}
Now the calling code can simple hook into the MyForm::ButtonKeyDown event
I'm not sure how you're wiring up the events with your class, but if you set the KeyPreview property of the form to True, you can grab a hold of the event there and then pass it along to your class that is processing the events. So even when the button has the focus, the KeyDown will fire the event on the form.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
... Invoke your class
}

Overriding MS Office Hotkeys

How might I go about programatically overriding hotkeys in MS Office?
I have a global hotkey (CTRL+SHIFT+1) for my app that works in other applications but seems to get lost when I try it in any MS Office application. When I shutdown my app the hotkeys work in MS Office again as they are supposed to.
You have to go with SetWindowsHookEx and set dwThreadId to 0.
So you can hook all key input from all thread (by this way, you can make a keylogger..)
please read http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
and
http://support.microsoft.com/kb/318804
Putting this here in case someone want to view another answer. This also descibes a hook.
It is possible to do this using a keyboard hook. A good hook class for this can be found on this CodeProject Article
Using the below code will prevent the WIN+LEFT or WIN+RIGHT from occurring. You can use this to override whichever keys you'd like.
This will even override hotkeys which you added via RegisterHotKey Win API.
Once you have those classes in your project you can add handlers to the static HookManager class like below.
//It's worth noting here that if you subscribe to the Key_Press event then it will break the international accent keys.
HookManager.KeyPress += HookManager_KeyPress;
HookManager.KeyDown += HookManager_KeyDown;
HookManager.KeyUp += HookManager_KeyUp;
You can also add mouse events, but for simplicity I'm just showing the keyboard hook.
I've also created a generic list so that I know which keys are currently down and I remove those keys from the list on the KeyUp event.
public static List<Keys> keysDown = new List<Keys>();
private static void HookManager_KeyDown(object sender, KeyEventArgs e)
{
//Used for overriding the Windows default hotkeys
if(keysDown.Contains(e.KeyCode) == false)
{
keysDown.Add(e.KeyCode);
}
if (e.KeyCode == Keys.Right && WIN())
{
e.Handled = true;
//Do what you want when this key combination is pressed
}
else if (e.KeyCode == Keys.Left && WIN())
{
e.Handled = true;
//Do what you want when this key combination is pressed
}
}
private static void HookManager_KeyUp(object sender, KeyEventArgs e)
{
//Used for overriding the Windows default hotkeys
while(keysDown.Contains(e.KeyCode))
{
keysDown.Remove(e.KeyCode);
}
}
private static void HookManager_KeyPress(object sender, KeyPressEventArgs e)
{
//Used for overriding the Windows default hotkeys
}
public static bool CTRL()
{
//return keysDown.Contains(Keys.LShiftKey)
if (keysDown.Contains(Keys.LControlKey) ||
keysDown.Contains(Keys.RControlKey) ||
keysDown.Contains(Keys.Control) ||
keysDown.Contains(Keys.ControlKey))
{
return true;
}
else
{
return false;
}
}
public static bool SHIFT()
{
//return keysDown.Contains(Keys.LShiftKey)
if (keysDown.Contains(Keys.LShiftKey) ||
keysDown.Contains(Keys.RShiftKey) ||
keysDown.Contains(Keys.Shift) ||
keysDown.Contains(Keys.ShiftKey))
{
return true;
}
else
{
return false;
}
}
public static bool WIN()
{
//return keysDown.Contains(Keys.LShiftKey)
if (keysDown.Contains(Keys.LWin) ||
keysDown.Contains(Keys.RWin))
{
return true;
}
else
{
return false;
}
}
public static bool ALT()
{
//return keysDown.Contains(Keys.LShiftKey)
if (keysDown.Contains(Keys.Alt))
{
return true;
}
else
{
return false;
}
}

Categories