Сосредоточьтесь на TextBox в DataTemplate - PullRequest
9 голосов
/ 01 декабря 2008

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

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

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

Ответы [ 4 ]

12 голосов
/ 26 июня 2010

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

Поскольку 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();
    }

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

9 голосов
/ 01 декабря 2008

Поскольку вы знаете имя 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 не было полностью инициализировано к моменту, когда мы ввели метод.

7 голосов
/ 10 августа 2012

Хорошо. Поэтому я думаю, что у меня есть лучшее решение. В любом случае это сработало для меня. У меня есть простой шаблон данных, в котором я хочу выделить текстовое поле. 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>
2 голосов
/ 09 сентября 2010

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

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