WPF TextBox and preserving tabs - wpf

Is there some elegant way how to allow pasting into WPF TextBox that has AcceptsTab set to false while preserving tabs in the pasted text ?
When AcceptsTab is true, then user can't use tabs to move to next control, which isn't desired by my users. But they want to have tabs that are pasted, which currently are replaced by spaces.
Thank you

I am not sure this qualifies as elegant but it works but might not be as complete as you want (e.g. when right-clicking in the textbox and selecting Paste from the context menu).
See Paste Event in a WPF TextBox
Set the AcceptsTab to true just before Ctrl-V is processed and restore it after:
XAML:
<TextBox AcceptsTab="False"
Height="200"
PreviewKeyDown="TextBox_PreviewKeyDown"
KeyUp="TextBox_KeyUp"/>
C#:
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (!(sender is TextBox textbox))
{
return;
}
if (e.Key == Key.V && (e.KeyboardDevice.IsKeyDown(Key.LeftCtrl) || e.KeyboardDevice.IsKeyDown(Key.RightCtrl)))
{
textbox.AcceptsTab = true;
}
}
private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
if (!(sender is TextBox textbox))
{
return;
}
textbox.AcceptsTab = false;
}
This could be turned into a behavior so it would be easier to apply it to more textboxes without writing code behind.

Another approach is by setting AcceptsTab to true and moving the focus when (Shift) Tab is pressed.
The nice side effect is that all the copy/paste scenarios will still function but the user will not be able to type a tab.
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
var textBox = sender as TextBox;
if (textBox != null)
{
var direction =
e.KeyboardDevice.IsKeyDown(Key.LeftShift) || e.KeyboardDevice.IsKeyDown(Key.RightShift)
? FocusNavigationDirection.Previous
: FocusNavigationDirection.Next;
textBox.MoveFocus(new TraversalRequest(direction));
}
}
}

Related

Item Right Tapped in ListView WPF

Is there a way to get OnItemRightTapped event on ListView or GridView that works exactly like ItemClick, except obviously react only on right tap?
You can add an event handler for mouse down and then determine the click source in the code:
private void listView_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Right)
{
// Do what u need to do here
}
else
e.Handled = true;
}

TextBox (PasswordBox) SelectAll method doesn't work

I have some concept question here. I know how to select all text in TextBox or in PasswordBox. Via GotKeyboardFocus and PreviewMouseLeftButtonDown events, you know. This works fine.
XAML:
PreviewMouseLeftButtonDown="PasswordOnPreviewMouseDown"
GotKeyboardFocus="SelectAllPassword"
CodeBehind
private void SelectAllPassword(Object sender, RoutedEventArgs e)
{
var pb = (sender as PasswordBox);
if (pb != null)
pb.SelectAll();
}
private void PasswordOnPreviewMouseDown(Object sender, MouseButtonEventArgs e)
{
var pb = (sender as PasswordBox);
if (pb != null)
if (!pb.IsKeyboardFocusWithin)
{
e.Handled = true;
pb.Focus();
}
}
But question is - why this doesn't work?
XAML:
PreviewMouseLeftButtonDown="PasswordOnPreviewMouseDown"
CodeBehind:
private void PasswordOnPreviewMouseDown(Object sender, MouseButtonEventArgs e)
{
_txtPassword.SelectAll();
e.Handled = true;
}
Where _txtPassword - TextBox or PasswordBox control. So why I'm enforsed to Focus text control?
Actually, the selection is working.
You may feel that the text is not selected because it's not visually highlighted, but that's because the TextBox is not focused.
Try giving focus to your TextBox with the Tab key, you'll see the whole text highlighted.

Textbox in ContextMenu loses focus on mouse move

I have a Textbox withing the ControlTemplate of a MenuItem, which is inside a ContextMenu. The Textbox works well and I can type in it properly. But if I move the mouse over any of the other menu items in the context menu, they claim focus and I lose focus from the textbox. At this point I have to click back into the textbox to continue typing.
Is there a pattern or accepted method of resolving this issue?
Thanks
If you want to take back focus, you can type as below.
textBox.CaptureMouse();
textBox.ReleaseMouseCapture();
I think it can catch focus to textbox.
Well after trying a few different things, I got something to work:
For all other menu items that can capture focus (on mouse enter), set e.Handled = true for the PriviewGoTKeyboardFocus event:
void menuItem_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}
One can do this automatically from a window base class by looping through all menu items in a context menu. This requires hijacking the tag for those menuitems in which you insert the textbox.
void contextMenu_Opened(object sender, RoutedEventArgs e)
{
ContextMenu contextMenu = sender as ContextMenu;
foreach (FrameworkElement frameworkElement in contextMenu.Items)
{
if (frameworkElement is MenuItem)
{
MenuItem menuItem = (frameworkElement as MenuItem);
if (!(menuItem.Tag != null && menuItem.Tag.ToString() == "MaintainFocus"))
menuItem.PreviewGotKeyboardFocus += new KeyboardFocusChangedEventHandler(menuItem_PreviewGotKeyboardFocus);
}
}
}
void contextMenu_Closed(object sender, RoutedEventArgs e)
{
ContextMenu contextMenu = sender as ContextMenu;
foreach (FrameworkElement frameworkElement in contextMenu.Items)
{
if (frameworkElement is MenuItem)
{
MenuItem menuItem = (frameworkElement as MenuItem);
if (!(menuItem.Tag != null && menuItem.Tag.ToString() == "MaintainFocus"))
menuItem.PreviewGotKeyboardFocus -= menuItem_PreviewGotKeyboardFocus;
}
}
}

Keeping keyboard focus on a single control while still beeing able to use a ListBox

Working on a TouchScreen application which also has a keyboard attached, I have the following problem:
The WPF window has a TextBox, which should receive ALL keyboard input. There are also Buttons and a ListBox, which are solely used by the TouchScreen(=Mouse).
A very simple example looks like this:
<Window x:Class="KeyboardFocusTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<StackPanel>
<TextBox Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus"/>
<Button Click="Button_Click">Add</Button>
<ListBox ItemsSource="{Binding Strings}" />
</StackPanel>
</Window>
To keep the TextBox always focused, I just do:
private void TextBox_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}
So far so good - the problem now is, that I can't select items from the ListBox anymore. This only seems to work, if the ListBox has the keyboard focus. But if I loose the keyboard focus on the TextBox, I can't enter text anymore without clicking it first.
Any ideas, comments suggestions are welcome!
There might be a more elegant solution for this, but you could always handle the PreviewKeyDown event at the Window level, and pass focus to the TextBox if it doesn't already have it, instead of preventing it from losing focus in the first place. That way, the ListBox can use focus as is normal, but as soon as a key is pressed focus jumps right to the TextBox. In addition, you can filter out keys that you don't want to switch focus - the arrow keys come to mind, which could then be used to move up and down in the ListBox.
Adding an event handler like the following should do the trick:
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (!textBox.IsFocused)
{
textBox.Focus();
}
}
Based on Nicholas' suggestion (thx!), here's a markup extension, which is used like:
<TextBox Helpers:KeyboardFocusAttractor.IsAttracted="true" />
It seems to work, and ANTS didn't show any memory leaks. But when it comes to WPF and especially events and bindings, you never know, so use with care!
public static class KeyboardFocusAttractor
{
public static readonly DependencyProperty IsAttracted = DependencyProperty.RegisterAttached("IsAttracted",
typeof (bool), typeof (KeyboardFocusAttractor), new PropertyMetadata(false, OnIsAttracted));
private static void OnIsAttracted(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var isAttracted = (bool) e.NewValue;
var controlWithInputFocus = d as Control;
if (controlWithInputFocus != null)
{
if (isAttracted)
{
new KeyboardInputFocusEventManager(controlWithInputFocus);
}
}
}
public static void SetIsAttracted(DependencyObject dp, bool value)
{
dp.SetValue(IsAttracted, value);
}
public static bool GetIsAttracted(DependencyObject dp)
{
return (bool) dp.GetValue(IsAttracted);
}
private class KeyboardInputFocusEventManager
{
private readonly Control _control;
private Window _window;
public KeyboardInputFocusEventManager(Control control)
{
_control = control;
_control.Loaded += ControlLoaded;
_control.IsVisibleChanged += ControlIsVisibleChanged;
_control.Unloaded += ControlUnloaded;
}
private void ControlLoaded(object sender, RoutedEventArgs e)
{
_window = Window.GetWindow(_control);
if (_window != null)
{
_control.Unloaded += ControlUnloaded;
_control.IsVisibleChanged += ControlIsVisibleChanged;
if (_control.IsVisible)
{
_window.PreviewKeyDown += ParentWindowPreviewKeyDown;
}
}
}
private void ControlUnloaded(object sender, RoutedEventArgs e)
{
_control.Unloaded -= ControlUnloaded;
_control.IsVisibleChanged -= ControlIsVisibleChanged;
}
private void ControlIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_window != null)
{
_window.PreviewKeyDown -= ParentWindowPreviewKeyDown;
}
if (_control.IsVisible)
{
_window = Window.GetWindow(_control);
if (_window != null)
{
_window.PreviewKeyDown += ParentWindowPreviewKeyDown;
}
}
}
private void ParentWindowPreviewKeyDown(object sender, KeyEventArgs e)
{
Keyboard.Focus(_control);
}
}
}

hiding grids using ESC key

I'm new to WPF.
I have like 15 grids on my Window and I have a small menu on which I can click and choose which grid to show up or hide. One grid at a time only. I would like that grid to hode (fade out) when I hit Esc. I have all the animations already, I just need to know what grid is visible (active) at the moment.
I don't know how to get current topmost control of my Window.
My solution is when KeyDown event is triggered on my Window to:
private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Escape)
{
//check all grids for IsVisible and on the one that is true make
BeginStoryboard((Storyboard)this.FindResource("theVisibleOne_Hide"));
}
}
By active, I assume that means the one that has keyboard focus. If so, the following will return the control that currently has keyboard input focus:
System.Windows.Input.Keyboard.FocusedElement
You could use it like this:
if (e.Key == System.Windows.Input.Key.Escape)
{
//check all grids for IsVisible and on the one that is true make
var selected = Keyboard.FocusedElement as Grid;
if (selected == null) return;
selected.BeginStoryboard((Storyboard)this.FindResource("HideGrid"));
}
An approach that would be more decoupled would be to create a static attached dependency property. It could be used like this (untested):
<Grid local:Extensions.HideOnEscape="True" .... />
A very rough implementation would look like:
public class Extensions
{
public static readonly DependencyProperty HideOnEscapeProperty =
DependencyProperty.RegisterAttached(
"HideOnEscape",
typeof(bool),
typeof(Extensions),
new UIPropertyMetadata(false, HideOnExtensions_Set));
public static void SetHideOnEscape(DependencyObject obj, bool value)
{
obj.SetValue(HideOnEscapeProperty, value);
}
public static bool GetHideOnEscape(DependencyObject obj)
{
return (bool)obj.GetValue(HideOnEscapeProperty);
}
private static void HideOnExtensions_Set(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = d as Grid;
if (grid != null)
{
grid.KeyUp += Grid_KeyUp;
}
}
private static void Grid_KeyUp(object sender, KeyEventArgs e)
{
// Check for escape key...
var grid = sender as Grid;
// Build animation in code, or assume a resource exists (grid.FindResource())
// Apply animation to grid
}
}
This would remove the need to have code in codebehind.

Resources