简体   繁体   English

WPF,treeview是mouseover,只有在第一次之后才起作用

[英]WPF, treeview is mouseover only works after first time

I have a treeview where I have added an eventsetter to the item container style to catch whenever a F1 is pressed while hovering a mouse over. 我有一个树形视图,其中在项目容器样式中添加了事件指示符,以将鼠标悬停在按F1时捕获。 So in the code behind I tried to find the child object that the mouse is over. 因此,在后面的代码中,我试图找到鼠标悬停的子对象。 The child object is only found in the tree after a node have been expanded and tried once before, the key down is catched correctly every time. 仅在扩展节点并尝试一次节点后,才能在树中找到子对象,每次都能正确捕获到按下键。 So it is only the second time the IsMouseOver child object is found. 因此,这只是第二次找到IsMouseOver子对象。

I have disabled virtualization for the target tree, but it doesn't make any difference. 我已经为目标树禁用了虚拟化,但是没有任何区别。

<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>

The Code behind event handler 事件处理程序背后的代码

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

Do any of you know of a magic trick here ? 你们中有人知道魔术吗? I have tried to do a TreeViewReportType.UpdateLayout() and also anItem.UpdateLayout() to see if it made any changes. 我尝试做一个TreeViewReportType.UpdateLayout()anItem.UpdateLayout()来查看它是否进行了任何更改。 But it didn't help. 但这没有帮助。

Tried to look on previous answers but it is datagrid related and is to disable virtualization, which doesn't work here ? 试图查看以前的答案,但它与数据网格相关,并且将禁用虚拟化,在这里不起作用?

Controls, such as TreeViewItems, have to have focus in order to receive keyboard events. 控件(例如TreeViewItems)必须具有焦点才能接收键盘事件。

You've got a few options here. 您在这里有一些选择。 You can put the event on the TreeView itself, but that only works if the treeview has keyboard focus. 您可以将事件放在TreeView本身上,但是仅在Treeview具有键盘焦点时才起作用。 As long as it's the only control in the window, you're fine. 只要它是窗口中的唯一控件,就可以了。 If it isn't, you're in trouble and you need to handle the key event at the window level. 如果不是,那么您就麻烦了,您需要在窗口级别处理键事件。 Another option would be to write a trigger in your ItemContainerStyle which gives the tree view items keyboard focus when IsMouseOver. 另一个选择是在ItemContainerStyle中编写一个触发器,该触发器在IsMouseOver时为树视图项提供键盘焦点。 But that's silly. 但这很愚蠢。

Let's do it at the window level, because we can generalize it: Wherever you press F1 in this window, we'll search the control under the mouse and its parents for one that has a DataContext with a HelpId property. 让我们在窗口级别进行操作,因为我们可以对其进行概括:无论您在此窗口中HelpId按F1键,我们都将在鼠标及其父控件下的控件中搜索具有带有HelpId属性的DataContext的HelpId If we find one, we'll use it. 如果找到一个,我们将使用它。 If ReportType is the only vm class that has a HelpId, it'll work fine. 如果ReportType是唯一具有HelpId的vm类,它将正常工作。 If you add HelpId to another class, that'll just work. 如果您将HelpId添加到另一个类,那将起作用。 If you decide to list ReportTypes in a ListView too, that'll just work. 如果您也决定在ListView中列出ReportType,那也可以。

This replaces all of the code in your question. 这将替换您问题中的所有代码。

MainWindow.xaml MainWindow.xaml

    ...
    PreviewKeyDown="Window_PreviewKeyDown"
    ...

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM