【问题标题】:How to change focus from textbox of one ListViewItem to another in UWP?如何在 UWP 中将焦点从一个 ListViewItem 的文本框更改为另一个?
【发布时间】:2019-08-26 03:22:13
【问题描述】:

我在ListView 中有一个TextBox。我在KeyDown 事件上动态添加ListView 项目(在将新项目添加到我的可观察集合时,由于双向绑定而创建了一个新的ListView 项目)。

现在,当添加新元素时,我想设置焦点到新创建的 ListView 项目的 TextBox。比我想象的有点复杂,帮我解决这个问题。

【问题讨论】:

    标签: c# visual-studio xaml uwp uwp-xaml


    【解决方案1】:

    我原本以为可以使用ListView.ContainerFromItem检索新添加的项目,然后使用VisualTreeHelper在模板中搜索TextBox并聚焦。然而事实证明,这个解决方案不起作用。添加项目后,列表项的容器不会立即具体化(这是合乎逻辑的,因为控件刚刚收到有关集合更改的通知,但还没有时间构建控件层次结构,就像我们的代码一样仍在执行中)。

    事实上,这个问题有一个更简单的解决方案。您可以在模板内的TextBox 上使用Loaded 事件。这只会在模板第一次具体化时调用一次。 但这不是一个完美的解决方案,请参阅下面的更新。

    在我的示例中,我有以下模板:

    <DataTemplate>
        <Grid>
            <TextBox Loaded="InputTextBox_Loaded" />
        </Grid>
    </DataTemplate>
    

    在代码隐藏中:

    private void InputTextBox_Loaded(object sender, RoutedEventArgs e)
    {
        var textBox = (TextBox)sender;
        textBox.Focus(FocusState.Programmatic);
    }
    

    更新:虚拟化

    原来有一个问题 - 虚拟化会在内存中创建多个模板副本(取决于窗口大小)以允许舒适的滚动,但之后它只会重用现有控件 - 以及 Loaded事件将永远不会被再次调用 - 这是一个问题。幸运的是,我们也可以解决这个问题 - 我们将使用 DataContextChanged 来代替 Loaded 事件。

    更新的 XAML:

    <DataTemplate>
        <Grid >
            <TextBox DataContextChanged="TextBox_DataContextChanged" />
        </Grid>
    </DataTemplate>
    

    更新的代码隐藏:

    private void TextBox_DataContextChanged(
         FrameworkElement sender, 
         DataContextChangedEventArgs args)
    {
        var textBox = (TextBox)sender;
        if ( args.NewValue == Items.Last())
        {
            //last item, focus it
            textBox.Focus(FocusState.Programmatic);
        }
    }
    

    好的,那就更好了,我们快到了!只剩下一件事让它变得完美。当前的配置意味着一旦我们将最后一个项目滚动到视图中,它将始终获得焦点,这可能不是我们想要的。相反,我们可能希望这种情况只发生一次 - 当新添加项目时。我们可以通过添加一个bool 标志来做到这一点,当我们将新项目添加到集合中时我们将其设置为true,并在我们第一次关注它时翻转回false

    //set this to true when a new item is being added to the collection
    private bool _focusItem = true;
    
    private void TextBox_DataContextChanged(
         FrameworkElement sender, 
         DataContextChangedEventArgs args)
    {
        var textBox = (TextBox)sender;
        if (args.NewValue == Items[Items.Count - 1] && _focusItem)
        {
            //last item, focus it
            textBox.Focus(FocusState.Programmatic);
            _focusItem = false;
        }
    }
    

    【讨论】:

    • @PrithviVenu 意识到我的解决方案还有一个问题,我将在大约 10 分钟内更新我的答案
    • @PrithviVenu 用我认为更通用的解决方案更新了我的答案。可能仍然可以改进一点 - 例如将新项目滚动到视图中以确保完成焦点并将_focusItem 设置回false。有关更多信息,请参阅此问题:stackoverflow.com/questions/43781501/…
    • 我尝试了您的解决方案,但您更新的解决方案对我不起作用
    • 被改变的数据上下文被称为唯一的第一次并且焦点没有改变,它保持在同一个第一个文本框中
    • 这很不寻常,你能在 GitHub 上创建一个示例 repo 以便我看一下吗?我在本地试过,效果很好
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 1970-01-01
    相关资源
    最近更新 更多