In short:
TreeView is bound to ObservableCollection of ViewModels. Click on a node with long lasting operation and moving mouse on other node while operation lasts, results in selecting the hovered node unintendedly (WPF does that, no mouseclick by user). -> Why, and how can I stop that?
Long version:
I have a TreeView with a Hierarchical-DataTemplate bound to an ObservableCollection of custom ViewModels. Building up the node structure works fine as well as running most of the commands.
Here are the events of the DataTemplate-Items that are bound to commands in the ViewModel using an EventToCommandBehaviour:
- PreviewMouseRightButtonDown
- PreviewMouseRightButtonUp
- PreviewMouseLeftButtonDown
- PreviewMouseLeftButtonUp
- PreviewMouseMove
- Drop
All of the commands are fired successfully. Here´s the XAML datatemplate:
<DataTemplate DataType="{x:Type model:TreeNodeBaseViewModel}">
<StackPanel Orientation="Horizontal" Height="16">
<StackPanel Orientation="Horizontal" Height="16" ToolTip="{Binding ToolTip}" IsHitTestVisible="True" Background="Transparent">
<Grid Height="16" Width="16" Margin="0,0,5,0" Visibility="{Binding ShowIcon, Converter={StaticResource BooleanToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
<Viewbox Width="{Binding PackIcon.Width}" Height="{Binding PackIcon.Height}">
<iconPacks:PackIconSimpleIcons Foreground="{Binding PackIcon.Color}" Rotation="{Binding PackIcon.Vector_Angle}" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="{Binding PackIcon.Value, Mode=OneWay}" />
</Viewbox>
</Grid>
<Label Content="{Binding DisplayName}" AllowDrop="{Binding IsDropAllowed}" Foreground="{Binding Color}" Padding="0"/>
<i:Interaction.Behaviors>
<beh:EventToCommandBehavior Command="{Binding PreviewMouseRightButtonDownCommand}" Event="PreviewMouseRightButtonDown" PassArguments="True" />
<beh:EventToCommandBehavior Command="{Binding PreviewMouseRightButtonUpCommand}" Event="PreviewMouseRightButtonUp" PassArguments="True" />
<beh:EventToCommandBehavior Command="{Binding PreviewMouseLeftButtonDownCommand}" Event="PreviewMouseLeftButtonDown" PassArguments="True" />
<beh:EventToCommandBehavior Command="{Binding PreviewMouseLeftButtonUpCommand}" Event="PreviewMouseLeftButtonUp" PassArguments="True" />
<beh:EventToCommandBehavior Command="{Binding PreviewMouseMoveCommand}" Event="PreviewMouseMove" PassArguments="True" />
<beh:EventToCommandBehavior Command="{Binding DropCommand}" Event="Drop" PassArguments="True" />
</i:Interaction.Behaviors>
</StackPanel>
<Button Background="Transparent" BorderThickness="0" Margin="5,0,0,0" Visibility="{Binding IsNodePropertyButtonVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
<iconPacks:PackIconMaterial Width="10" Height="10" Foreground="LightGray" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="InformationOutline" />
<i:Interaction.Behaviors>
<beh:EventToCommandBehavior Command="{Binding PropertiesCommand}" Event="Click" PassArguments="True" />
</i:Interaction.Behaviors>
</Button>
</StackPanel>
</DataTemplate>
The following TreeViewItem properties are also bound to the ViewModel:
- IsEnabled
- AllowDrop
- IsExpanded
- IsSelected
- Tag
Here´s the XAML:
<Style x:Key="MenuItemTemplateItemContainerStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu" Value="{DynamicResource MenuItemContextMenu}"/>
<Setter Property="IsEnabled" Value="{Binding IsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Setter Property="AllowDrop" Value="{Binding IsDropAllowed, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Setter Property="Tag" Value="{Binding}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<Trigger Property="beh:TreeNodeMouseOver.IsMouseDirectlyOverItem" Value="True">
<Setter Property="Background" Value="AliceBlue" />
</Trigger>
</Style.Triggers>
</Style>
My Problem is:
When I left click on a node that triggers a longer running operation two things happen:
- The IsSelected in the ViewModel is not always set, although it is bound to the IsSelected of the TreeViewItem
- If I move the mouse on another node whilst the operation of the other node is still running the selection will change to select the hovered node without my doing anything. Why is this happening?
A few things I´ve I´ve tried:
- Set IsSelected manually if it has not been set – works ok, but shouldn´t have to be done.
- By debugging and looking at the call stack when IsSelected changes I found out that WPF seems to trigger Select and ChangeSelection when another node gets the focus – but why? …and how can I suppress this?
- Played around with TreeViewItem.GotFocus event to prevent the unintended selection
Here´s part of the call stack when IsSelected is set unintentionally. WPF seems to trigger Select and ChangeSelection after OnGotFocus is called:
[Native to Managed Transition]
[Managed to Native Transition]
PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.SetValue(object item, object value) Unknown
PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.UpdateValue(object value) Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateSource(object value = false) Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.UpdateValue() Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateOverride() Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Update() Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.ProcessDirty() Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Dirty() Unknown
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.SetValue(System.Windows.DependencyObject d, System.Windows.DependencyProperty dp, object value) Unknown
WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp = {System.Windows.DependencyProperty}, object value = false, System.Windows.PropertyMetadata metadata = {System.Windows.FrameworkPropertyMetadata}, bool coerceWithDeferredReference = false, bool coerceWithCurrentValue = false, System.Windows.OperationType operationType = Unknown, bool isInternal) Unknown
WindowsBase.dll!System.Windows.DependencyObject.SetValue(System.Windows.DependencyProperty dp, object value) Unknown
PresentationFramework.dll!System.Windows.Controls.TreeView.ChangeSelection(object data = {HOSEC.UI.Tree.ViewModels.TreeNodeMainNodeViewModel}, System.Windows.Controls.TreeViewItem container = {System.Windows.Controls.TreeViewItem}, bool selected = true) Unknown
PresentationFramework.dll!System.Windows.Controls.TreeViewItem.Select(bool selected = true) Unknown
PresentationFramework.dll!System.Windows.Controls.TreeViewItem.OnGotFocus(System.Windows.RoutedEventArgs e = {System.Windows.RoutedEventArgs}) Unknown
PresentationCore.dll!System.Windows.UIElement.IsFocused_Changed(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) Unknown
WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) Unknown
PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) Unknown
WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args) Unknown
WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp = {System.Windows.DependencyProperty}, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry = {System.Windows.EffectiveValueEntry}, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) Unknown
WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal) Unknown
WindowsBase.dll!System.Windows.DependencyObject.SetValue(System.Windows.DependencyPropertyKey key, object value) Unknown
PresentationCore.dll!System.Windows.Input.FocusManager.OnFocusedElementChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e) Unknown
...