[英]WPF: Move focus to the next item in an ItemsControl on enter
WPF:當用戶在ItemsControl的texbox內按下Enter鍵時,我想將焦點移到ItemsControl的下一個項目的文本框,或者如果用戶在最后一個項目中,則創建一個新的文本框。
更清楚地說:
場景1
ItemsControl items:
[ textbox in item 1 ] <- user is here
[ textbox in item 2 ]
[ textbox in item 3 ]
按Enter鍵后:
[ textbox in item 1 ]
[ textbox in item 2 ] <- user is here
[ textbox in item 3 ]
場景2
ItemsControl項目:
[ textbox in item 1 ]
[ textbox in item 2 ]
[ textbox in item 3 ] <- user is here
按Enter鍵后:
[ textbox in item 1 ]
[ textbox in item 2 ]
[ textbox in item 3 ]
[ textbox in item 4 ] <- user is here
如果有幫助,這是項目數據模板的代碼:
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Path=PartName, FallbackValue='----',TargetNullValue='----', NotifyOnSourceUpdated=True}" KeyDown="TextBox_KeyDown"/>
<Button Grid.Column="1" FontSize="10" x:Name="DeletePartButton" Click="DeletePartButton_Click" Height="22">Usuń</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
編輯2:我使用ItemsControl,因為不需要選擇功能。
編輯3:我找到了部分解決方案。 它用於將焦點移到下一個元素,而不是新元素(這是這里最重要的功能)
private void PartNameTextBox_KeyDown(object sender, KeyEventArgs e)
{
var box = (TextBox)sender;
if (e.Key == Key.Enter)
{
var part = (PiecePart)box.DataContext;
int index = part.ParentPiece.Parts.IndexOf(part);
if (index == part.ParentPiece.PartCount - 1)
{
part.ParentPiece.Parts.Add(new PiecePart(GetNewPartName(part.ParentPiece)));
bool success = PartListBox.ApplyTemplate();
// try to force wpf to build a visual tree for the new item success = false :(
}
// throws out of bounds exception if a new item was added (and wasn't added to a visual tree)
var el = ((UIElement)VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(PartListBox, 0),0),1),0),0),++index),0),0));
el.Focus();
}
}
將焦點移到WPF中下一個元素的正確方法是使用TraversalRequest
類(它表示將焦點移到另一個控件的請求)和FocusNavigationDirection
枚舉 , 該 枚舉 指定用戶界面(UI)內所需焦點更改請求的方向。被嘗試 。 此示例摘自MSDN上的TraversalRequest
類頁面:
// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = _focusMoveValue;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
// Change keyboard focus.
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
我知道了。 為了將焦點移至下一個元素,我使用了Sheridan的解決方案。 要將焦點移到新元素,我使用了“添加”標志和TextBox.Loaded事件。
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Background="White" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="PartNameTextbox" Text="{Binding Path=PartName, FallbackValue='----',TargetNullValue='----', NotifyOnSourceUpdated=True}" KeyDown="PartNameTextBox_KeyDown"
Loaded="PartNameTextbox_Loaded"/>
<Button Grid.Column="1" FontSize="10" x:Name="DeletePartButton" Click="DeletePartButton_Click" Height="22">Usuń</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
后台代碼:
bool partAdding = false;
private void PartNameTextBox_KeyDown(object sender, KeyEventArgs e)
{
var box = (TextBox)sender;
if (e.Key == Key.Enter)
{
var part = (PiecePart)box.DataContext;
int index = part.ParentPiece.Parts.IndexOf(part);
if (index == part.ParentPiece.PartCount - 1)
{
part.ParentPiece.Parts.Add(new PiecePart(GetNewPartName(part.ParentPiece)));
UpdateCurrentLine(part.ParentPiece);
partAdding = true;
}
// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = FocusNavigationDirection.Down;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
// Change keyboard focus.
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
}
}
private void PartNameTextbox_Loaded(object sender, RoutedEventArgs e)
{
if (partAdding)
{
var box = ((TextBox)sender);
var pp = ((PiecePart) box.DataContext);
if (pp.IsLastPart)
{
box.Focus();
box.SelectionStart = box.Text.Length;
partAdding = false;
}
}
}
在TextBox的PreviewKeyDown
上
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
var txt= sender as TextBox;
var selecteditem=FindParent<ListBoxItem>(txt);
int index = ListBox.ItemContainerGenerator.IndexFromContainer(selecteditem);
if(index<ListBox.Items.Count)
{
var afterItem=(ListBoxItem)ListBox.ItemContainerGenerator.ContainerFromIndex(index+1);
TextBox tbFind = GetDescendantByType(afterItem, typeof (TextBox), "TextBox") as TextBox;
if (tbFind != null)
{
FocusHelper.Focus(tbFind);
}
}
}
}
public static Visual GetDescendantByType(Visual element, Type type, string name)
{
if (element == null) return null;
if (element.GetType() == type)
{
FrameworkElement fe = element as FrameworkElement;
if (fe != null)
{
if (fe.Name == name)
{
return fe;
}
}
}
Visual foundElement = null;
if (element is FrameworkElement)
(element as FrameworkElement).ApplyTemplate();
for (int i = 0;
i < VisualTreeHelper.GetChildrenCount(element);
i++)
{
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = GetDescendantByType(visual, type, name);
if (foundElement != null)
break;
}
return foundElement;
}
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
另一個將焦點設置在文本框上的助手:
public static class FocusHelper
{
public static void Focus(UIElement element)
{
element.Dispatcher.BeginInvoke(DispatcherPriority.Input, new ThreadStart(delegate()
{
element.Focus();
}));
}
}
Listbox.SelectedIndex = 0;
private void Listbox_OnKeyUp(object sender, KeyEventArgs e)
{
if (e.Key== Key.Enter)
{
if(Listbox.Items.Count-1>Listbox.SelectedIndex)
Listbox.SelectedIndex++;
else
Listbox.SelectedIndex=0;
}
}
當用戶專注於您的列表框時,此功能有效。
C#
private void ListBox_KeyUp(object sender, KeyEventArgs e)
{
int selectIndex = listBox.SelectedIndex;
int listItemCount = listBox.Items.Count;
if (e.Key == Key.Enter)
{
if (selectIndex == listItemCount - 1)
{
ListBoxItem newItem = new ListBoxItem();
newItem.Content = "Your Content";
listBox.Items.Add(newItem);
listBox.SelectedItem = newItem;
}
else
{
listBox.SelectedIndex = selectIndex + 1;
}
}
}
XAML
<ListBox HorizontalAlignment="Left" Name="listBox" Height="92" KeyUp="ListBox_KeyUp">
<ListBoxItem Content="First"/>
<ListBoxItem Content="Second"/>
<ListBoxItem Content="Third"/>
<ListBoxItem Content="Fourth"/>
</ListBox>
首先,我獲得列表框項目計數為listItemCount ,還獲得所選項目索引為selectIndex ,然后檢查關鍵事件為Enter,然后檢查是否到達列表的最后一個。 如果是,請創建一個新的列表框項目並將其添加到列表中。
ar.gorgin的解決方案進行了一些細微的更改,主要是考慮到OP需要它在ItemsControl而不是ListBox上工作的事實:
if (e.Key == Key.Enter)
{
var txt = sender as TextBox;
var selecteditem = FindParent<ContentPresenter>(txt);
int index = myItemsCtl.ItemContainerGenerator.IndexFromContainer(selecteditem);
if (index < myItemsCtl.Items.Count)
{
var afterItem = myItemsCtl.ItemContainerGenerator.ContainerFromIndex(index + 1) as Visual;
TextBox tbFind = GetDescendantByType(afterItem, typeof(TextBox), "txtBoxName") as TextBox;
if (tbFind != null)
{
FocusHelper.Focus(tbFind);
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.