【问题标题】:Items added to the ListBox at runtime never gets focused在运行时添加到 ListBox 的项目永远不会获得焦点
【发布时间】:2016-06-01 07:51:46
【问题描述】:

如上图所示,我在列表框中有三列。

第一列是名字,第二列是姓氏,第三列是年龄。

当我在年龄列上按 Enter 时,一个新的人被添加到列表中。这最终反映了 ListBox 的变化。

问题:

当我在年龄列上按 Enter 时,我按预期添加了一个新人。但焦点不会转到下一个 ListItem。无论我按 Enter 多少次,我都不会关注以编程方式添加的项目。

示例:

我创建了一个重现该问题的示例项目:

Download Sample Project

我有一个 ListBox 如下:

<ListBox ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson}">

    <ListBox.Resources>
        <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="True">
                    <Setter Property="IsSelected" Value="True"></Setter>
                </Trigger>
            </Style.Triggers>
            <Setter Property="Focusable" Value="False" />
        </Style>
    </ListBox.Resources>

    <ListBox.ItemTemplate>

        <DataTemplate>

            <Grid x:Name="CurrentItemGrid">

                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="200" />
                    <ColumnDefinition Width="100" />
                </Grid.ColumnDefinitions>

                <TextBox Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" Tag="IgnoreEnterKeyTraversal">

                    <TextBox.InputBindings>
                        <KeyBinding Command="{Binding DataContext.DeleteUnwantedOrderItemTransactionCommand, 
                                                              RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                            Gesture="Return" />
                        <KeyBinding Command="{Binding DataContext.DeleteUnwantedOrderItemTransactionCommand, 
                                                              RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                            Gesture="Tab" />
                    </TextBox.InputBindings>

                </TextBox>

                <TextBox Grid.Column="1" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" Margin="5,0"/>

                <TextBox Grid.Column="2" Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" Margin="5,0" Tag="IgnoreEnterKeyTraversal">

                    <TextBox.InputBindings>
                        <KeyBinding Command="{Binding DataContext.AddNewOrderItemTransactionCommand, 
                                                              RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                            Gesture="Return" />
                        <KeyBinding Command="{Binding DataContext.AddNewOrderItemTransactionCommand, 
                                                              RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                            Gesture="Tab" />
                    </TextBox.InputBindings>
                </TextBox>

            </Grid>

        </DataTemplate>

    </ListBox.ItemTemplate>

</ListBox>

在 ViewModel 中:

public class MainWindowViewModel : INotifyPropertyChanged
{
    IEventAggregator eventAggregator;

    public MainWindowViewModel(IEventAggregator _eventAggregator)
    {
        People = new ObservableCollection<Person>();
        People.Add(new Person());
        eventAggregator = _eventAggregator;

        DeleteUnwantedOrderItemTransactionCommand = new RelayCommand(DeleteUnwantedOrderItemTransaction);
        AddNewOrderItemTransactionCommand = new RelayCommand(AddNewOrderItemTransaction);
    }

    public RelayCommand DeleteUnwantedOrderItemTransactionCommand { get; set; }
    public RelayCommand AddNewOrderItemTransactionCommand { get; set; }

    private ObservableCollection<Person> _People;

    public ObservableCollection<Person> People
    {
        get
        {
            return _People;
        }
        set
        {
            if (_People != value)
            {
                _People = value;
                OnPropertyChanged("People");
            }
        }
    }

    private Person _SelectedPerson;

    public Person SelectedPerson
    {
        get
        {
            return _SelectedPerson;
        }
        set
        {
            if (_SelectedPerson != value)
            {
                _SelectedPerson = value;
                OnPropertyChanged("SelectedPerson");
            }
        }
    }

    protected void DeleteUnwantedOrderItemTransaction(object obj)
    {
        if (SelectedPerson.FirstName == "")
        {
            People.Remove(SelectedPerson);
        }

        if (People.Count == 0)
        {
            People.Add(new Person());
        }

        eventAggregator.GetEvent<ChangeFocusToNextUIElementEvent>().Publish(true);
    }

    protected void AddNewOrderItemTransaction(object obj)
    {
        if (SelectedPerson == People.Last())
            People.Add(new Person());

        eventAggregator.GetEvent<ChangeFocusToNextUIElementEvent>().Publish(true);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

在代码隐藏中:

public partial class MainWindow : Window
{
    IEventAggregator _eventAggregator = new EventAggregator();

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel(_eventAggregator);
        _eventAggregator.GetEvent<ChangeFocusToNextUIElementEvent>().Subscribe(MoveToNextUIElement);
    }

    void MoveToNextUIElement(bool obj)
    {
        // Gets the element with keyboard focus.
        UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;

        if (elementWithFocus != null)
        {
            elementWithFocus.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }
    }
}

【问题讨论】:

    标签: c# wpf xaml listbox


    【解决方案1】:

    在您触发事件的那一刻,您已将元素添加到 VM,但它可能尚未由视图 (ListBox) 绑定和创建。以低优先级调度焦点请求可能会有所帮助。还要检查是否可以通过按 TAB 访问元素,以便知道遍历是否有效。

    elementWithFocus.Dispatcher.Invoke(() => 
        elementWithFocus.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)),
        DispatcherPriority.Input); // !
    

    ListBox 元素的虚拟化也可能是一个问题。您必须将添加的项目显示在视图中。

    干杯。

    【讨论】:

    • 不,我无法通过按 TAB 键访问该元素,无论按多少次 TAB 键。我尝试使用您的代码,但它给了我错误--->无法将 lambda 表达式转换为 Delegate,因为它不是委托类型。如果您可以花一些时间,请下载问题中链接的我的示例项目。
    • 我已经解决了您代码中的错误。缺少一个括号。我对此感到抱歉。但我仍然无法通过 listBox 中新添加的行进行 TAB。
    • 如果 TAB 不起作用, TraversalRequest 也不起作用。您最好的选择是获取 ListBoxItem 并直接调用 Focus()。如果您想要连续的 Tab-Sequence,您也可以尝试在您的项目上使用 KeyboardNavigationMode。
    • 你给了我提示,问题就解决了。我使用了 KeyboardNavigation.TabNavigation="Continue",它按预期工作。
    • 事实上,我需要结合使用 Dispatcher.Invoke 和 KeyboardNavigation.TabNavigation="Continue" 来获得预期的输出。如果有人需要工作样本,可以在这里找到:drive.google.com/file/d/0B5WyqSALui0bTXFGZWxQUWVRdkU/…
    猜你喜欢
    • 2017-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    • 2021-04-15
    • 1970-01-01
    相关资源
    最近更新 更多