如何使 TreeviewItem 处于未选中状态,但当用户在 wpf 树视图中按向上/向下箭头键时处于焦点

How to make TreeviewItem unselected but focused when the user presses up/down arrow keys within wpf treeview

提问人:UGURENG 提问时间:6/7/2023 最后编辑:UGURENG 更新时间:6/14/2023 访问量:31

问:

我有一个树视图由 4 个 manuel treeviewItems 组成,分别标记如下图所示。

enter image description here

假设我选择了 Item1.1 一次。

如您所见,如果将鼠标悬停在其他 treeviewItems 上,则从技术上讲可以更改颜色。

我想做的是通过按键盘上的向下箭头键或向上箭头键来执行相同的操作。

在 wpf treeview 中按向上/向下箭头键的专用行为就像我正在选择所需的 treeviewItem 一样。

我希望每当用户使用向下/向上箭头键浏览 treeviewItems 时,从键盘按 Enter 键时,都会选择任何所需的 TreeViewItem。

我希望模仿任何所需的 TreeViewItem,就好像用户通过按向下/向上箭头键将鼠标悬停在所需的 TreeviewItem 上一样。

如何将此行为更改为所需的行为?

这在技术上可行吗?

 <Grid>
        <TreeView x:Name="exampleTreeview" KeyboardNavigation.TabNavigation="Cycle" PreviewKeyDown="exampleTreeview_PreviewKeyDown" ItemsSource="{Binding Items}">
           
          
            <TreeView.ItemContainerStyle>
                <Style TargetType="TreeViewItem">
                    <Style.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="common:MyTreeViewHelper.IsMouseDirectlyOverItem" Value="True"/>
                                <Condition Property="TreeViewItem.Focusable" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Background"  Value="#a826A0Da"/>
                            <Setter Property="BorderBrush"  Value="#a826A0Da"/>
                        </MultiTrigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                      Color="Blue" />
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}"
                      Color="Black" />
                <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
                      Color="DarkGray" />
                <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}"
                      Color="Black" />


                <HierarchicalDataTemplate DataType="{x:Type model:TreeItemModel}" ItemsSource="{Binding Items}">
                        <StackPanel Orientation="Horizontal" >
                            <Grid>
                                <StackPanel Orientation="Horizontal">

                                <TextBlock    x:Name="textBlockHeader" Text="{Binding Name}" Margin="3,0">
                                    
                                </TextBlock>  
                                                                                  
                                                                                      
                                </StackPanel>

                            </Grid>
                        </StackPanel>


                    </HierarchicalDataTemplate>
                </TreeView.Resources>

            <TreeView.InputBindings>
                <KeyBinding Key="Down"  Command="{Binding ControlDownCommand}" Modifiers="Ctrl"  >

                </KeyBinding>
            </TreeView.InputBindings>
        </TreeView>
    </Grid>
public class MainViewModel:INotifyPropertyChanged
    {
        public ICommand ControlDownCommand
        {
            get
            {
                return new RelayCommand<KeyBinding>(x => OnControlDown(x));
            }
        }
        public MainViewModel()
        {
            var items13 = new ObservableCollection<TreeItemModel>{
                                new TreeItemModel("Item 1.1"),
                                new TreeItemModel("Item 1.2"),
                                new TreeItemModel("Item 1.3"),
                                new TreeItemModel("Item 1.4")};

          

            this.Items = items13;

        }

        public ObservableCollection<TreeItemModel> Items { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnControlDown(KeyBinding keyBinding)
        {
            Console.Write("HELLO");
        }
    }
 public class TreeItemModel
    {
        public string Name { get; set; }
        public TreeItemModel(string name)
        {
           this.Name = name;    
        }

       
    }
 public static class MyTreeViewHelper
    {
        //
        // The TreeViewItem that the mouse is currently directly over (or null).
        //
        private static TreeViewItem _currentItem = null;

        //
        // IsMouseDirectlyOverItem:  A DependencyProperty that will be true only on the 
        // TreeViewItem that the mouse is directly over.  I.e., this won't be set on that 
        // parent item.
        //
        // This is the only public member, and is read-only.
        //

        // The property key (since this is a read-only DP)
        private static readonly DependencyPropertyKey IsMouseDirectlyOverItemKey =
            DependencyProperty.RegisterAttachedReadOnly("IsMouseDirectlyOverItem",
                                                typeof(bool),
                                                typeof(MyTreeViewHelper),
                                                new FrameworkPropertyMetadata(null, new CoerceValueCallback(CalculateIsMouseDirectlyOverItem)));

        // The DP itself
        public static readonly DependencyProperty IsMouseDirectlyOverItemProperty =
            IsMouseDirectlyOverItemKey.DependencyProperty;

        // A strongly-typed getter for the property.
        public static bool GetIsMouseDirectlyOverItem(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsMouseDirectlyOverItemProperty);
        }

        // A coercion method for the property
        private static object CalculateIsMouseDirectlyOverItem(DependencyObject item, object value)
        {
            // This method is called when the IsMouseDirectlyOver property is being calculated
            // for a TreeViewItem.  

            if (item == _currentItem)
                return true;
            else
                return false;
        }

        //
        // UpdateOverItem:  A private RoutedEvent used to find the nearest encapsulating
        // TreeViewItem to the mouse's current position.
        //

        private static readonly RoutedEvent UpdateOverItemEvent = EventManager.RegisterRoutedEvent(
            "UpdateOverItem", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyTreeViewHelper));

        //
        // Class constructor
        //

        static MyTreeViewHelper()
        {
            // Get all Mouse enter/leave events for TreeViewItem.
            EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.MouseEnterEvent, new MouseEventHandler(OnMouseTransition), true);
            EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.MouseLeaveEvent, new MouseEventHandler(OnMouseTransition), true);

            // Listen for the UpdateOverItemEvent on all TreeViewItem's.
            EventManager.RegisterClassHandler(typeof(TreeViewItem), UpdateOverItemEvent, new RoutedEventHandler(OnUpdateOverItem));
        }


        //
        // OnUpdateOverItem:  This method is a listener for the UpdateOverItemEvent.  When it is received,
        // it means that the sender is the closest TreeViewItem to the mouse (closest in the sense of the tree,
        // not geographically).

        static void OnUpdateOverItem(object sender, RoutedEventArgs args)
        {
            // Mark this object as the tree view item over which the mouse
            // is currently positioned.
            _currentItem = sender as TreeViewItem;

            // Tell that item to re-calculate the IsMouseDirectlyOverItem property
            _currentItem.InvalidateProperty(IsMouseDirectlyOverItemProperty);

            // Prevent this event from notifying other tree view items higher in the tree.
            args.Handled = true;
        }

        //
        // OnMouseTransition:  This method is a listener for both the MouseEnter event and
        // the MouseLeave event on TreeViewItems.  It updates the _currentItem, and updates
        // the IsMouseDirectlyOverItem property on the previous TreeViewItem and the new
        // TreeViewItem.

        static void OnMouseTransition(object sender, MouseEventArgs args)
        {
            lock (IsMouseDirectlyOverItemProperty)
            {
                if (_currentItem != null)
                {
                    // Tell the item that previously had the mouse that it no longer does.
                    DependencyObject oldItem = _currentItem;
                    _currentItem = null;
                    oldItem.InvalidateProperty(IsMouseDirectlyOverItemProperty);
                }

                // Get the element that is currently under the mouse.
                IInputElement currentPosition = Mouse.DirectlyOver;

                // See if the mouse is still over something (any element, not just a tree view item).
                if (currentPosition != null)
                {
                    // Yes, the mouse is over something.
                    // Raise an event from that point.  If a TreeViewItem is anywhere above this point
                    // in the tree, it will receive this event and update _currentItem.

                    RoutedEventArgs newItemArgs = new RoutedEventArgs(UpdateOverItemEvent);
                    currentPosition.RaiseEvent(newItemArgs);

                }
            }
        }
    }
WPF 键盘 树视图 KeyDown KeyUp

评论


答: 暂无答案