使用FocusManager,ContentControl和DataTemplate进行聚焦

时间:2013-02-05 15:48:50

标签: wpf focus datatemplate focusmanager

我正在尝试通过以下结构的应用程序使(键盘)焦点正常工作:

ItemsControl
  DataTemplate for Item
    ContentControl
      DataTemplate for Content (multiple, depending on type)
        Some container
          Different controls (mainly textbox or OK button should have focus)

在每个内容模板的根目录(“Some container”)上,我将FocusManager.FocusedElement设置为模板中的一个控件。 ItemsControl中的所有项目中,只有一个ContentControl实际可见(“当前”)。所以,基本上,我首先看到它,其中的OK按钮有焦点,用户可以按Enter确认。然后隐藏这一个,显示下一个,并且其主控件(例如文本框)具有焦点,用户可以按回车确认(小事件处理使文本框中的输入确认该项目)等等。 / p>

完成所有操作后,用户可以单击工具栏中的按钮重新开始,使第一个ContentControl再次可见。但这一次它没有焦点。我不知道谁有焦点,我希望它像开始时一样工作。我试图将焦点设置为刚刚显示的ContentControl(通过虚线可见),但它不会将焦点设置为它的子节点。由于我不知道选择了哪个数据模板(好吧,不是没有编写特殊代码),我不能手动设置焦点或通过绑定到子节点。也许我现在错过了一件简单的事情?

另一方面,当用户手动点击活动的ContentControl按钮或文本框时,他可以使用回车确认,但是然后ItemsControl似乎有焦点,而不是下一个项目。我无法理解这个焦点的事情。有简单的解决方案吗?我是否必须编写代码来从ContentControl遍历树以获取正确的datatemplate?然后,我可以只关注数据模板或设置为FocusedElement的控件,还是必须分别为每个数据模板编写代码?

1 个答案:

答案 0 :(得分:0)

您应该实现一个动作,可以在需要时从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