我用的Mvvm框架.
我的ListBox有十行三列的文本框,回车会跳到下一个,一列到底回车跳到下一列,到最后一个又会跳到第一个.并且跳到文本框文本框中的文字会被全部选中.代码如下.
xmal
<ListBox ItemsSource="{Binding XXX}" x:Name="lb" SelectionMode="Single" Grid.Row="1" BorderThickness="0">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border BorderThickness="1,0,1,1" BorderBrush="#999999" HorizontalAlignment="Stretch" Margin="0" Padding="0">
<Grid HorizontalAlignment="Stretch" Height="54">
<Grid.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource tbx}">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontSize" Value="28" />
<Setter Property="Width" Value="Auto" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="79" />
<ColumnDefinition />
<ColumnDefinition Width="79" />
<ColumnDefinition />
<ColumnDefinition Width="79" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding No1}" />
<TextBox Text="{Binding t1}" Grid.Column="1" Width="282" Height="53" BorderThickness="0" >
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding DataContext.MoveFocusCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
<TextBox.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.TextChangedCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock Text="{Binding No2}" Grid.Column="2" />
<TextBox Text="{Binding t2}" Grid.Column="3" Width="282" Height="53" BorderThickness="0" >
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding DataContext.MoveFocusCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</TextBox.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.TextChangedCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock Text="{Binding No3}" Grid.Column="4" />
<TextBox Text="{Binding t3}" Grid.Column="5" Width="282" Height="53" BorderThickness="0" >
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding DataContext.MoveFocusCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</TextBox.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataContext.TextChangedCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<Border BorderThickness="1" BorderBrush="#999999" Grid.Column="1" HorizontalAlignment="Left" Width="1" />
<Border BorderThickness="1" BorderBrush="#999999" Grid.Column="2" HorizontalAlignment="Left" Width="1" />
<Border BorderThickness="1" BorderBrush="#999999" Grid.Column="3" HorizontalAlignment="Left" Width="1" />
<Border BorderThickness="1" BorderBrush="#999999" Grid.Column="4" HorizontalAlignment="Left" Width="1" />
<Border BorderThickness="1" BorderBrush="#999999" Grid.Column="5" HorizontalAlignment="Left" Width="1" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
xmal.cs 这里也可以ListBox的Loaded事件,就不用给ListBox取名字了.暂时还不知这两种有什么区别,哪种更好一点.
Loaded += (s, e) => { Vm.Load(lb); };
ViewModel.cs
System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<TextBox>> boxlist { get; set; } = new System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<TextBox>>();
public void Load(ListBox lb)
{
boxlist = Util.FindTextBoxesInListBox(lb);
}
/// <summary>
/// 文本框回车向下
/// </summary>
public ICommand MoveFocusCommand => new RelayCommand<TextBox>(textBox =>
{
if (textBox == null) return;
// 获取当前ListBox
var listBox = FindVisualParent<ListBox>(textBox);
if (listBox == null) return;
// 获取当前行和列
var currentItem = textBox.DataContext;
var currentColumn = Grid.GetColumn(textBox);
// 获取所有行
var items = listBox.Items.Cast<object>().ToList();
// 查找当前行索引
var currentIndex = items.IndexOf(currentItem);
// 计算下一行索引
var nextIndex = currentIndex + 1;
if (nextIndex >= 10)
{
// 如果已经是最后一行,跳转到下一列的第一行
nextIndex = 0;
currentColumn += 2; // 列间隔为2(因为列定义是交替的)
}
// 如果列超出范围,回到第一列
if (currentColumn > 5)
{
currentColumn = 1;
}
// 获取下一行的文本框
var nextTextBox = boxlist.FirstOrDefault(x => x.Key == currentColumn).Value[nextIndex];
if (nextTextBox != null)
{
nextTextBox.Focus();
nextTextBox.SelectAll();
}
else
{
// 如果找不到对应列的文本框,继续查找
MoveFocusCommand.Execute(nextTextBox);
}
});
// Helper methods
private static T FindVisualParent<T>(DependencyObject child) where T : DependencyObject
{
var parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
return parentObject is T parent ? parent : FindVisualParent<T>(parentObject);
}
private static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
try
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T result) { return result; }
var childResult = FindVisualChild<T>(child);
if (childResult != null) { return childResult; }
}
}
catch (Exception)
{
throw;
}
return null;
}
Util.cs
public static Dictionary<int, List<TextBox>> FindTextBoxesInListBox(ListBox listBox)
{
// 创建一个字典,用于存储按列索引分组的 TextBox
var doubleTextBoxColumns = new Dictionary<int, List<TextBox>>();
// 遍历 ListBox 中的每一项
for (int i = 0; i < listBox.Items.Count; i++)
{
// 获取 ListBoxItem
var listBoxItem = listBox.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
if (listBoxItem != null)
{
// 查找 Grid
var grid = FindVisualChilds<Grid>(listBoxItem);
if (grid != null)
{
// 获取每个 TextBox
FindDoubleTextBoxesInGrid(grid, doubleTextBoxColumns);
}
}
}
return doubleTextBoxColumns;
}
// 查找 Grid 中的所有 TextBox,并按列索引分组
private static void FindTextBoxesInGrid(Grid grid, Dictionary<int, List<TextBox>> doubleTextBoxColumns)
{
// 遍历 Grid 的所有子元素
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(grid); i++)
{
var child = VisualTreeHelper.GetChild(grid, i);
// 检查是否为 TextBox
if (child is TextBox doubleTextBox)
{
// 获取该 TextBox 所在的列索引
int column = Grid.GetColumn(doubleTextBox);
// 确保字典中有该列
if (!doubleTextBoxColumns.ContainsKey(column))
{
doubleTextBoxColumns[column] = new List<TextBox>();
}
// 将 TextBox 添加到对应的列列表中
doubleTextBoxColumns[column].Add(doubleTextBox);
}
// 如果子元素是 Grid,递归查找
if (child is Panel panel)
{
FindDoubleTextBoxesInGrid(panel as Grid, doubleTextBoxColumns);
}
}
}
// 通用查找视觉子元素的方法
private static T FindVisualChilds<T>(DependencyObject parent) where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T typedChild)
{
return typedChild;
}
var childOfChild = FindVisualChilds<T>(child);
if (childOfChild != null)
{
return childOfChild;
}
}
return null;
}