WPF,treeview只有鼠标悬停才能在第一次使用

时间:2018-05-23 14:25:03

标签: c# wpf ismouseover

我有一个树视图,我在项目容器样式中添加了一个事件设置器,以便在鼠标悬停时按下F1时捕获。所以在后面的代码中我试图找到鼠标结束的子对象。只有在节点扩展并尝试过一次之后,才会在树中找到子对象,每次都会正确捕获密钥。所以这只是第二次找到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()来查看是否进行了更改。但它没有帮助。

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

1 个答案:

答案 0 :(得分:1)

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

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

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

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

MainWindow.xaml

    ...
    PreviewKeyDown="Window_PreviewKeyDown"
    ...

MainWindow.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);
    }
}

Helpers.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;
    }
}