Есть TextBox-ы в одном Grid. По атрибуту PreviewKeyDown в TextBox перемещается курсор из одного TextBox в другой:
private void Cell21_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
Cell22.Focus();
e.Handled = true;
}
private void Cell22_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
Cell21.Focus();
e.Handled = true;
}
Есть ли способ добавить атрибут PreviewKeyDown к этому Grid, чтобы не прописывать команды в каждом методе отдельно, а просто в одном методе добавить List из TextBox-ов и перемещать курсор в цикле?
<Grid Name="FirstGrid" PreviewKeyDown="FirstGrid_PreviewKeyDown">
<TextBox Padding="1" Margin="2" FontSize="35" Name="Cell1" TextAlignment="Center" PreviewKeyDown="Cell1_PreviewKeyDown"/>
<TextBox Padding="1" Margin="2" FontSize="35" Name="Cell2" TextAlignment="Center" PreviewKeyDown="Cell2_PreviewKeyDown"/>
</Grid>
Если да, то какое условие нужно добавить в if, в этом методе?
private void FirstGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
List<TextBox> firstList = new List<TextBox> { Cell1, Cell2 };
for (int i = 0; i < firstList.Count; i++) {
if (e.Key == Key.Space) {
firstList[i].Focus();
e.Handled = true;
}
}
}
Для того, чтобы внутри одного контейнера фокус перемещался циклически, необходимо установить Attached Property KeyboardNavigation.TabNavigation:
<Grid KeyboardNavigation.TabNavigation="Cycle">
...
</Grid>
Для передачи фокуса следующему элементу используйте метод MoveFocus:
TextBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
Для того, чтобы передавать фокус по пробелу, как вам ответили в предыдущем вопросе, необходимо обрабатывать событие PreviewKeyDown, если вам нужно это сделать более чем в одном контроле, то правильнее всего будет написать соответствующее поведение для контрола. Для этого подключите библиотеку System.Windows.Interactivity.dll одним из способов, описанных в этом ответе и напишите такой класс:
public class SpaceNavigationBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewKeyDown += OnPreviewKeyDown;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewKeyDown -= OnPreviewKeyDown;
}
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Space) return;
AssociatedObject.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
}
}
Теперь, в разметке окна подключите пространство имен:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Проверяем:
<StackPanel>
<StackPanel KeyboardNavigation.TabNavigation="Cycle">
<TextBox>
<i:Interaction.Behaviors>
<local:SpaceNavigationBehavior/>
</i:Interaction.Behaviors>
</TextBox>
<TextBox>
<i:Interaction.Behaviors>
<local:SpaceNavigationBehavior/>
</i:Interaction.Behaviors>
</TextBox>
</StackPanel>
<TextBox>
<i:Interaction.Behaviors>
<local:SpaceNavigationBehavior/>
</i:Interaction.Behaviors>
</TextBox>
</StackPanel>
Запустите проект и убедитесь, что фокус (как по пробелу, так и по Tab) перемещается циклически между первыми двумя TextBox'ами.
К сожалению AP Interaction.Behaviors не имеет сеттера, поэтому невозможно установить ему значение в стиле для всех TextBox сразу. Поэтому я написал вот такое (не самое лучшее, но рабочее) решение:
public static class BehaviorEx
{
public static Type GetBehavior(DependencyObject obj)
=> (Type)obj.GetValue(BehaviorProperty);
public static void SetBehavior(DependencyObject obj, Type value)
=> obj.SetValue(BehaviorProperty, value);
public static readonly DependencyProperty BehaviorProperty =
DependencyProperty.RegisterAttached("Behavior", typeof(Type), typeof(BehaviorEx),
new PropertyMetadata(null, OnBehaviorChanged));
private static void OnBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var type = (Type)e.NewValue;
var collection = Interaction.GetBehaviors(d);
if (e.NewValue != null)
{
var behavior = (Behavior)Activator.CreateInstance(type);
collection.Add(behavior);
}
}
}
После чего разметку можно сократить до такой:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBox">
<Setter Property="local:BehaviorEx.Behavior" Value="local:SpaceNavigationBehavior"/>
</Style>
</StackPanel.Resources>
<StackPanel KeyboardNavigation.TabNavigation="Cycle">
<TextBox/>
<TextBox/>
</StackPanel>
<TextBox/>
</StackPanel>
Работает она так же, как и предыдущая.
Если у вас будет всего 2 TextBox, как в вопросе, то достаточно будет сделать так:
bool firstCellIsFocused = true;
private void FirstGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
if (firstCellIsFocued)
{
Cell2.Focus();
}
else
{
Cell1.Focus();
}
e.Handled = true;
firstCellIsFocued = !firstCellIsFocued;
}
}
UPD
Если TextBox много, то лучше в каждом TextBox подписаться на событие PreviewKeyDown
<StackPanel>
<TextBox PreviewKeyDown="Cell_PreviewKeyDown"/>
<TextBox PreviewKeyDown="Cell_PreviewKeyDown"/>
<TextBox PreviewKeyDown="Cell_PreviewKeyDown"/>
</StackPanel>
и перемещать фокус как сказано в ответе у @Андрей NOP:
private void Cell_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
TextBox tb = sender as TextBox;
tb.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
}
}
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости