I have a treeview consists of 4 manuel treeviewItems labelled as shown below respectively.
Lets assume that I have selected Item1.1 once.
If I hover the mouse over the other treeviewItems, as you can see, it is technically possible to change the colour.
What I want to do is to do the same by pressing Down Arrow or Up Arrow keys from keyboard.
The dedicated behaviour of pressing up/down arrow keys within wpf treeview is as if I am selecting the desired treeviewItem.
I would like any desired TreeViewItem to be selected whenever the user presses Enter key from keyboard while navigating through the treeviewItems by using arrow down/up keys.
I would like any desired TreeViewItem to be imitated as if the user hovers the mouse over the desired TreeviewItem by pressing down/up arrow keys.
How can I change this behaviour into the desired behavior?
Is it technically possible?
<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);
}
}
}
}
