【问题标题】:In a ListBox with TextBoxes, how do I focus on the added TextBox?在带有 TextBoxes 的 ListBox 中,如何将注意力集中在添加的 TextBox 上?
【发布时间】:2019-11-14 04:10:02
【问题描述】:

我有一个文本框列表框和一个用于向列表框添加新项目的按钮。当我点击添加按钮时,我希望新项目被选中并且包含的​​文本框具有焦点。

我可以通过绑定轻松选择新项目,但我还没有弄清楚如何聚焦文本框。我无法从视图模型中执行此操作,因为新文本框要到稍后才会创建。

为了向您展示问题,我创建了一个示例解决方案(可在 GitHub 上获得:TestListBoxWithTextBox)。

这是 XAML:

<Window
    x:Class="TestListBoxWithTextBox.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"
    >
    <StackPanel>
        <ListBox
            ItemsSource="{Binding Items}"
            SelectedItem="{Binding SelectedItem}"
            >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Name}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button
            Content="Add"
            Command="{Binding AddItemCommand}"
            />
    </StackPanel>
</Window>

这是我的视图模型(ViewModelBase来自MVVM Light Toolkit):

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<ItemViewModel> Items { get; } =
        new ObservableCollection<ItemViewModel>();

    private ItemViewModel selectedItem;
    public ItemViewModel SelectedItem
    {
        get => selectedItem;
        set => Set(nameof(SelectedItem), ref selectedItem, value);
    }

    public ICommand AddItemCommand =>
        new RelayCommand(() =>
        {
            var newItem = new ItemViewModel();
            Items.Add(newItem);
            SelectedItem = newItem;
        });
}

public class ItemViewModel : ViewModelBase
{
    private string name;
    public string Name
    {
        get => name;
        set => Set(nameof(Name), ref name, value);
    }
}

我尝试过的事情

从视图模型与视图对话:我使用方法FocusTextBox(int index) 创建了一个接口IView。视图实现了这个接口,视图模型在选择新项目后调用它的方法(在AddItemCommand中)。但是,视图无法找到 TextBox,因为它还不存在,即使我将新项目添加到 Items 列表。

TextBox 集中在其Loaded 事件上: Loaded 事件保证TextBox 存在,因此尝试将焦点放在那里是有意义的。这适用于这个简单的示例,但不适用于我的实际程序。在我的程序中,当您在TextBox 中输入内容然后按下添加按钮时,焦点从文本框移开,然后调用单独的命令对项目进行排序。在排序期间,一些文本框被移动,这会导致它们的Loaded 事件被调用,然后从添加按钮中窃取焦点,因此它的行为就像从未按下添加按钮一样。

TextBox 聚焦在ListBox.ItemContainingGenerator.StatusChanged 事件上: 当列表框中的选定项发生变化时,我会监听ItemContainingGenerator.StatusChanged 事件。当列表框中的项目正在生成或完成生成时会触发此事件。当它们完成生成后,我在所选项目(在视图模型中设置)中找到 TextBox 并专注于它。该解决方案有效,但并非始终如一。在我的某些列表框中,这不起作用,我不确定为什么。

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    我对你的代码做了一些小改动......我认为它可以工作......

    试试这个:

     <ListBox x:Name="MyListBox"
            ItemsSource="{Binding Items}"
            SelectedItem="{Binding SelectedItem}"
            >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBox MinWidth="100" Text="{Binding Name}" Initialized="TextBoxElement_OnInitialized"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    

    在您的代码隐藏中:

     private int _textBoxCounter = 0;
            private void TextBoxElement_OnInitialized(object sender, EventArgs e)
            {
                if (_textBoxCounter < MyListBox.Items.Count)
                {
                    Keyboard.Focus((IInputElement)sender);
                    _textBoxCounter++;
                }
    
            }
    

    这样你保证在排序操作时不会设置焦点

    【讨论】:

    • 不幸的是,这在我的实际程序中不起作用。正如我所提到的,当在文本框中键入某些内容并且它失去焦点时(例如按下添加按钮时),它可能会被移动到另一个位置以满足排序顺序。当这个移动发生时,被移动的文本框的 Initialized 事件被触发,使它获得焦点,因此失去了 Add 按钮的焦点,所以它的行为就像它从未被按下一样。
    • 我已经更新了我的答案......请查看它......我认为绑定的 mode=oneTime 会做到......
    • 感谢您的耐心等待,我已经更新了我的答案......这可能会有所帮助......请让我现在......
    • 还是不行。文本框被重新排序并显示一个新的,但新的没有焦点。
    • 我认为如果你提供排序命令会更清楚
    猜你喜欢
    • 2011-11-25
    • 1970-01-01
    • 2018-04-20
    • 2017-08-12
    • 2023-03-17
    • 2021-06-15
    • 2020-03-16
    • 2020-11-06
    • 1970-01-01
    相关资源
    最近更新 更多