WPF: Dataound TreeViewItem -> нежелательный выбор зависшего узла, когда ранее был выбран другой узел с длительной операцией - PullRequest
0 голосов
/ 16 января 2019

Короче говоря:

TreeView привязан к ObservableCollection ViewModels. Кликните по узлу с длительной операцией и переместите мышь на другой узел, пока операция длится, приводит к непреднамеренному выбору наведенного узла (WPF делает это, без щелчка мышью пользователем). -> Почему и как я могу это остановить?

Длинная версия:

У меня есть TreeView с Hierarchical-DataTemplate, привязанным к ObservableCollection пользовательских ViewModels. Построение структуры узла прекрасно работает, а также выполняет большинство команд.

Вот события DataTemplate-Items, которые связаны с командами в ViewModel с помощью EventToCommandBehaviour:

  • PreviewMouseRightButtonDown
  • PreviewMouseRightButtonUp
  • PreviewMouseLeftButtonDown
  • PreviewMouseLeftButtonUp
  • PreviewMouseMove
  • Капля

Все команды выполняются успешно. Вот табличка с данными XAML:

<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>

Следующие свойства TreeViewItem также связаны с ViewModel:

  • IsEnabled
  • AllowDrop
  • IsExpanded
  • IsSelected
  • Tag

Вот 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>

Моя проблема:

Когда я щелкаю левой кнопкой мыши по узлу, который запускает более продолжительную операцию, происходят две вещи:

  1. IsSelected в ViewModel не всегда устанавливается, хотя оно привязано к IsSelected объекта TreeViewItem
  2. Если я переместлю мышь на другой узел, в то время как работа другого узла все еще выполняется, выбор изменится, чтобы выбрать зависший узел без моего участия. Почему это происходит?

Несколько вещей, которые я пробовал:

  1. Установить IsSelected вручную, если он не был установлен - работает нормально, но делать это не обязательно.
  2. Путем отладки и просмотра стека вызовов при изменении IsSelected я обнаружил, что WPF, по-видимому, вызывает Select и ChangeSelection, когда другой узел получает фокус - но почему? ... и как я могу это подавить?
  3. Поиграл с событием TreeViewItem.GotFocus, чтобы предотвратить непреднамеренный выбор

Вот часть стека вызовов, когда IsSelected установлен непреднамеренно. WPF, кажется, вызывает Select и ChangeSelection после вызова OnGotFocus:

[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
...

1 Ответ

0 голосов
/ 17 января 2019

Хорошо, это определенно странное поведение WPF, но я исправил это сейчас:

Прежде всего, я деактивировал все команды, кроме PreviewMouseLeftButtonDownCommand, и обнаружил, что TreeView теряет фокус во время длительной операции. При попытке вернуть фокус по какой-то причине WPF устанавливает фокус на текущий зависший узел - который воспроизводим и не требует взаимодействия с пользователем.

Я ввел логическое значение DisableAutoSelection в MainViewModel, а затем запретил обновление значения IsSelected в TreeNodeViewModel, если DisableAutoSelection имеет значение true.

Также я переместил код в PreviewMouseLeftButtonDownCommand TreeNodeViewModel в асинхронную задачу и установил DisableAutoSelection = true перед запуском задачи. Когда ожидание задачи завершено, я устанавливаю DisableAutoSelection = false.

Вуаля, теперь это работает, и время случайно выбранных узлов прошло: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...