Сосредоточьтесь на TextBox в DataTemplate

У меня DataTemplate, содержащий TextBox. Я устанавливаю этот шаблон на элемент списка при выборе.

Я не могу установить фокус на текстовое поле в шаблоне. Я попытался вызвать MyTemplate.FindName, но в итоге получилось исключение недопустимой операции: эта операция действительна только для элементов, к которым применен этот шаблон.

Как я могу получить к нему доступ?


person Dusan Kocurek    schedule 30.11.2008    source источник


Ответы (4)


Поскольку вы знаете имя TextBox, на котором хотите сфокусироваться, это становится относительно легко. Идея состоит в том, чтобы получить шаблон в том виде, в каком он применяется к самому ListBoxItem.

Первое, что вам нужно сделать, это получить выбранный элемент:

var item = listBox1.ItemContainerGenerator.ContainerFromItem(listBox1.SelectedItem) as ListBoxItem;

Затем вы можете передать это в эту небольшую вспомогательную функцию, которая фокусирует элемент управления на основе его имени:

public void FocusItem(ListBoxItem item, string name)
{
    if (!item.IsLoaded)
    {
        // wait for the item to load so we can find the control to focus
        RoutedEventHandler onload = null;
        onload = delegate
        {
            item.Loaded -= onload;
            FocusItem(item, name);
        };
        item.Loaded += onload;
        return;
    }

    try
    {
        var myTemplate = FindResource("MyTemplateKey") as FrameworkTemplate; // or however you get your template right now

        var ctl = myTemplate.FindName(name, item) as FrameworkElement;
        ctl.Focus();
    }
    catch
    {
        // focus something else if the template/item wasn't found?
    }
}

Я предполагаю, что самая сложная задача - дождаться загрузки предмета. Мне пришлось добавить этот код, потому что я вызывал его из события ItemContainerGenerator.StatusChanged, а иногда ListBoxItem не был полностью инициализирован к тому времени, когда мы вошли в метод.

person Matt Hamilton    schedule 30.11.2008
comment
FindResource возвращает объект, поэтому обязательно приведите его к FrameworkTemplate. - person Michael; 23.11.2011

Я знаю, что это устарело, но сегодня я столкнулся с этой проблемой и в конце концов пришел к следующему решению:

Поскольку TextBox загружается только тогда, когда элемент выбран, и именно тогда вы хотите установить фокус, вы можете просто обработать событие TextBox.Load и вызвать Focus().

Этого можно добиться двумя способами.

1. Замените TextBox в DataTemplate на AutoFocusTextBox.

public class AutoFocusTextBox : TextBox
{
    public AutoFocusTextBox()
    {
        Loaded += delegate { Focus(); }; 
    }
}

Не забывайте, что вам нужно будет сослаться на пространство имен, в котором AutoFocusTextBox определен в вашем файле .xaml.

2. Добавьте обработчик в программный код файла, в котором определено DataTemplate.

SomeResourceDictionary.xaml

<TextBox Text="{Binding Something, Mode=TwoWay}" Style={StaticResource ...
        Loaded="FocusTextBoxOnLoad" />

SomeResourceDictionary.xaml.cs

    private void FocusTextBoxOnLoad(object sender, RoutedEventArgs e)
    {
        var textbox = sender as TextBox;
        if(textbox == null) return;
        textbox.Focus();
    }

С любым вариантом вы всегда можете добавить другое поведение в обработчике, например выделение всего текста.

person Jay    schedule 25.06.2010

В порядке. Так что я думаю, что у меня есть лучшее решение. В любом случае это сработало для меня. У меня есть простой шаблон данных, в котором я хочу сосредоточить внимание на текстовом поле. FocusManager переключает фокус на текстовое поле.

<DataTemplate x:Key="MyDataTemplate" DataType="ListBoxItem">
    <Grid>
        <WrapPanel Orientation="Horizontal" FocusManager.FocusedElement="{Binding ElementName=tbText}">
            <CheckBox IsChecked="{Binding Path=Completed}" Margin="5" />
            <Button Style="{StaticResource ResourceKey=DeleteButtonTemplate}" Margin="5" Click="btnDeleteItem_Click" />
            <TextBox Name="tbText" 
                     Text="{Binding Path=Text}" 
                     Width="200" 
                     TextWrapping="Wrap" 
                     AcceptsReturn="True" 
                     Margin="5" 
                     Focusable="True"/>
            <DatePicker Text="{Binding Path=Date}" Margin="5"/>
        </WrapPanel>
    </Grid>
</DataTemplate>
person Jesse Seger    schedule 10.08.2012

Второе предложение Джея изящно - и его можно обобщить, используя UIElement, а не TextBox, так что любой элемент управления можно легко сделать по умолчанию:

private void FocusControlOnLoad(object sender, RoutedEventArgs e)
{
    var uiElement = sender as UiElement;
    if(uiElement == null) return;
    uiElement.Focus();
}
person Pete    schedule 09.09.2010