WPF:将焦点移动到输入中的ItemsControl中的下一个项目

时间:2015-03-13 08:58:33

标签: c# wpf listbox

WPF:当用户在ItemsControl中的texbox内按下回车键时,我想将焦点移动到ItemsControl中下一个项目的文本框中,或者如果用户在最后一个项目中则创建一个新文本框。

更清楚:

SCENARIO 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();
        }
    }

6 个答案:

答案 0 :(得分:1)

将焦点移动到WPF中的下一个元素的正确方法是使用{em>表示将焦点移动到另一个控件的请求的TraversalRequest类和FocusNavigationDirection Enumeration 指定尝试所需焦点更改请求的用户界面(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);
} 

答案 1 :(得分:1)

我明白了。为了将焦点移到下一个元素,我使用Sheridan的解决方案。要将焦点移动到新元素,我使用&#34;添加&#34; flag和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;
        }
    }
}

答案 2 :(得分:1)

在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);
}

在TextBox上设置Focus的另一个帮助:

public static class FocusHelper
{
public static void Focus(UIElement element)
{
 element.Dispatcher.BeginInvoke(DispatcherPriority.Input, new ThreadStart(delegate()
 {
    element.Focus();
 }));
}
}

答案 3 :(得分:0)

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;
    }
}

当用户专注于您的列表框时,这项工作。

答案 4 :(得分: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然后检查我们是否到达列表的最后一个。如果它是一个新的列表框项目并将其添加到列表中。

答案 5 :(得分:0)

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);
        }
    }
}