Фокусировка с помощью FocusManager, ContentControl и DataTemplate
Я пытаюсь заставить (клавиатурный) фокус работать правильно с приложением следующей структуры:
ItemsControl
DataTemplate for Item
ContentControl
DataTemplate for Content (multiple, depending on type)
Some container
Different controls (mainly textbox or OK button should have focus)
В корне каждого шаблона содержимого ("Некоторый контейнер") я устанавливаю FocusManager.FocusedElement
к одному элементу управления в шаблоне. Из всех элементов в ItemsControl фактически виден только один из ContentControl ("текущий"). Итак, в основном, я делаю первое, что видно, кнопка ОК в нем имеет фокус, пользователь может нажать Enter для подтверждения. Затем этот скрыт, следующий показан, и его основной элемент управления (например, текстовое поле) имеет фокус, пользователь может нажать клавишу ввода для подтверждения (небольшая обработка события, чтобы ввести в текстовом поле подтверждение элемента) и так далее.
Когда все сделано, пользователь может нажать кнопку на панели инструментов, чтобы начать все сначала, снова делая первый ContentControl видимым. Но на этот раз у него нет фокуса. Я не знаю, у кого есть фокус, я бы хотел, чтобы он работал как в начале. Я попытался установить фокус на только что показанный ContentControl (который работает, видимый через пунктирную линию), но он не установит фокус на своих детей. Поскольку я не знаю, какой из шаблонов данных выбран (ну, не без написания специального кода), я не могу просто установить фокус вручную или с помощью привязки к ребенку. Может быть, я упускаю простую вещь на данный момент?
С другой стороны, когда пользователь вручную нажимает на активную кнопку ContentControl или текстовое поле, он может затем подтвердить ввод, но тогда, кажется, что у ItemsControl есть фокус, а не следующий элемент. Я просто не могу разобраться с этим вопросом. Есть ли простое решение? Нужно ли писать код для обхода дерева из ContentControl, чтобы получить правильную таблицу данных? И затем, я могу просто как-то сфокусировать табличку с данными или элемент управления, установленный как FocusedElement, или мне нужно написать код для каждой таблицы данных отдельно?
1 ответ
Вы должны реализовать действие, которое может быть запущено из ViewModel для фокусировки элемента при необходимости. Попробуйте этот код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interactivity;
using System.Windows;
namespace Behaviors
{
/// <summary>
/// Action to focus target element, if invoked when target is not yet specified, will focus once the target is set
/// </summary>
public class FocusAction : TargetedTriggerAction<DependencyObject>
{
protected override void OnAttached()
{
base.OnAttached();
}
protected override void OnDetaching()
{
base.OnDetaching();
}
protected override void Invoke(object parameter)
{
if (Target is UIElement)
{
if(!((UIElement)Target).IsKeyboardFocusWithin)
((UIElement)Target).Focus();
}
else
invokeOnConnect = true;
}
private bool invokeOnConnect = false;
protected override void OnTargetChanged(DependencyObject oldTarget, DependencyObject newTarget)
{
base.OnTargetChanged(oldTarget, newTarget);
if (invokeOnConnect)
{
invokeOnConnect = false;
Invoke(null);
}
}
}
}
А потом в WPF:
<DockPanel>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<nuib:FocusAction TargetName="title"/>
</i:EventTrigger>
<TextBlock x:Name="title"/>
</DockPanel>
пространства имен:
xmlns:nuib="clr-namespace:Behaviors;assembly=Nui" // for Action class
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" // .net library for interactivity