0

我有一个树视图,我在其中向项目容器样式添加了一个事件设置器,以便在鼠标悬停时按下 F1 时捕获。所以在后面的代码中我试图找到鼠标所在的子对象。子对象仅在节点展开并尝试过一次后才在树中找到,每次都正确捕获 key down。所以这只是第二次IsMouseOver找到子对象。

我已经为目标树禁用了虚拟化,但它没有任何区别。

<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
  <EventSetter Event="PreviewKeyDown" Handler="EventSetter_OnHandler"></EventSetter>
  <Setter Property="IsSelected">
    <Setter.Value>
      <MultiBinding Mode="OneWay" Converter="{StaticResource ActiveReportTypeMatchToBoolConverter}">
        <Binding Path="DataContext.ActiveReportType" ElementName="TreeViewExpander" />
        <Binding />
      </MultiBinding>
    </Setter.Value>
  </Setter>
  <Setter Property="UIElement.Uid" Value="{Binding Name}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>

事件处理程序背后的代码

private void EventSetter_OnHandler(object sender, KeyEventArgs e) {
        if (e.Key == Key.F1) {
            foreach (var item in TreeViewReportType.Items) {
                TreeViewItem anItem = TreeViewReportType.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                if (anItem?.IsMouseOver == true) {
                    foreach (ReportType childItem in anItem.Items) {
                        TreeViewItem childTreeViewItem = anItem.ItemContainerGenerator.ContainerFromItem(childItem) as TreeViewItem;
                        if (childTreeViewItem?.IsMouseOver == true) {
                            ApplicationCommands.Help.Execute(childItem.HelpId, childTreeViewItem);                                                              
                        }
                    }                                               
                    return;
                }                   
            }               
        }
    }

你们有谁知道这里的魔术吗?我试图做一个TreeViewReportType.UpdateLayout(),也anItem.UpdateLayout()看看它是否做了任何改变。但这没有帮助。

试图查看以前的答案,但它与数据网格相关并且是禁用虚拟化,这在这里不起作用?

4

1 回答 1

1

控件(例如 TreeViewItems)必须具有焦点才能接收键盘事件。

你在这里有几个选择。您可以将事件放在 TreeView 本身上,但这仅在树视图具有键盘焦点时才有效。只要它是窗口中唯一的控件,就可以了。如果不是,你就有麻烦了,你需要在窗口级别处理关键事件。另一种选择是在您的 ItemContainerStyle 中编写一个触发器,该触发器在 IsMouseOver 时为树视图项目提供键盘焦点。但这很愚蠢。

让我们在窗口级别执行此操作,因为我们可以对其进行概括:无论您在此窗口中的何处按 F1,我们都会在鼠标及其父项下的控件中搜索具有HelpId属性的 DataContext 的控件。如果我们找到一个,我们会使用它。如果ReportType是唯一具有 HelpId 的 vm 类,它会正常工作。如果您将 HelpId 添加到另一个类,那就行了。如果您决定在 ListView 中也列出 ReportTypes,那将是可行的。

这将替换您问题中的所有代码。

主窗口.xaml

    ...
    PreviewKeyDown="Window_PreviewKeyDown"
    ...

主窗口.xaml.cs

private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.F1)
    {
        var window = sender as Window;
        var point = Mouse.GetPosition(window);

        Helpers.ExecuteHelpUnderPoint(window, point);
    }
}

助手.cs

public static class Helpers
{
    public static void ExecuteHelpUnderPoint(FrameworkElement parent, Point point)
    {
        var hittestctl = parent.InputHitTest(point) as FrameworkElement;

        var helpID = GetNearestDataContextHelpID(hittestctl);

        if (helpID != null)
        {
            ApplicationCommands.Help.Execute(helpID, hittestctl);
        }
    }

    public static IEnumerable<T> GetAncestorsOfType<T>(DependencyObject dobj) where T : DependencyObject
    {
        dobj = VisualTreeHelper.GetParent(dobj);

        while (dobj != null)
        {
            if (dobj is T t)
                yield return t;

            dobj = VisualTreeHelper.GetParent(dobj);
        }
    }

    public static Object GetNearestDataContextHelpID(DependencyObject dobj)
    {
        var dataContexts = GetAncestorsOfType<FrameworkElement>(dobj)
            .Select(fe => fe.DataContext).Where(dc => dc != null);

        //  LINQ distinct probably doesn't affect order, but that's not guaranteed.
        //  https://stackoverflow.com/a/4734876/424129
        object prev = null;
        foreach (var dc in dataContexts)
        {
            if (dc != prev)
            {
                var prop = dc.GetType().GetProperty("HelpId");
                if (prop != null)
                {
                    var value = prop.GetValue(dc);
                    if (value != null)
                    {
                        return value;
                    }
                }
                prev = dc;
            }
        }

        return null;
    }
}
于 2018-05-23T15:35:00.347 回答