我有一个树视图,我在项目容器样式中添加了一个事件设置器,以便在鼠标悬停时按下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()
来查看是否进行了更改。但它没有帮助。
试图查看以前的答案,但它与数据网格相关并禁用虚拟化,这在这里不起作用?
答案 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;
}
}