WPF, TreeView работает только при первом запуске - PullRequest
0 голосов
/ 23 мая 2018

У меня есть древовидная структура, в которой я добавил Eventsetter в стиль контейнера элемента, чтобы перехватывать нажатие клавиши F1 при нажатии клавиши 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 голосов
/ 23 мая 2018

Элементы управления, такие как TreeViewItems, должны иметь фокус для получения событий клавиатуры.

У вас есть несколько вариантов здесь.Вы можете поместить событие в само TreeView, но это работает, только если у дерева есть фокус клавиатуры.Пока это единственный элемент управления в окне, ты в порядке.Если это не так, у вас проблемы, и вам нужно обработать ключевое событие на уровне окна.Другой вариант заключается в том, чтобы написать триггер в вашем ItemContainerStyle, который дает фокус на клавиатуре древовидных представлений при IsMouseOver.Но это глупо.

Давайте сделаем это на уровне окна, потому что мы можем обобщить его: где бы вы ни нажимали F1 в этом окне, мы будем искать элемент управления под мышью и его родителями для элемента, который имеет DataContext с HelpId имущество.Если мы найдем один, мы будем использовать его.Если ReportType является единственным классом vm, который имеет HelpId, он будет работать нормально.Если вы добавите HelpId в другой класс, это будет работать.Если вы решите также включить ReportTypes в ListView, это будет работать.

Это заменяет весь код в вашем вопросе.

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