18

Sometimes right-clicking treeviewitem results unhandled InvalidOperationException. In code behind I select the right clicked row:

    static TreeViewItem VisualUpwardSearch(DependencyObject source)
    {
        while (source != null && !(source is TreeViewItem))
            source = VisualTreeHelper.GetParent(source);

        return source as TreeViewItem;
    }

    private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
            TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);

            if (treeViewItem != null)
            {
                treeViewItem.Focus();
                e.Handled = true;
            }
    }

According to stacktrace above is the source of the problem.

xaml:

<UserControl.Resources>
   <HierarchicalDataTemplate ItemsSource="{Binding ClassesItemsSource}" DataType="{x:Type pnls:FavoriteObjectTableViewModel}">
        <StackPanel Orientation="Horizontal">
            <Image Source="{Binding Converter={StaticResource nameToBitmapSource}}" DataContext="{Binding Bitmap}" />
            <Label Content="{Binding TableName}"/>
        </StackPanel>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type pnls:FavoriteObjectClassViewModel}">
        <StackPanel Orientation="Horizontal">
            <Image Source="{Binding Bitmap, Converter={StaticResource UriToCachedImageConverter}}"/>
            <Label Content="{Binding ClassName}"/>
        </StackPanel>
    </DataTemplate>
</UserControl.Resources>

<TreeView Name="Insert_ObjectTreeIE" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding TablesItemsSource}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="TreeViewItem">
                <Setter Property="IsSelected" Value="{Binding IsSelected}" />
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                <EventSetter Event="PreviewMouseRightButtonDown" Handler="OnPreviewMouseRightButtonDown"></EventSetter>
                <EventSetter Event="MouseDoubleClick" Handler="OnMouseDoubleClick" />
            </Style>
        </TreeView.ItemContainerStyle>
</TreeView>

Stacktrace:

e.StackTrace    "   at MS.Internal.Media.VisualTreeUtils.AsVisual(DependencyObject element, Visual& visual, Visual3D& visual3D)\r\n   
at MS.Internal.Media.VisualTreeUtils.AsNonNullVisual(DependencyObject element, Visual& visual, Visual3D& visual3D)\r\n
at System.Windows.Media.VisualTreeHelper.GetParent(DependencyObject reference)\r\n   
at Tekla.Nis.Application.Shared.UI.Panels.FavoriteObjectsView.VisualUpwardSearch(DependencyObject source) in c:\\XXX\\161wpf\\src\\SharedAppFeature\\Panels\\FavoriteObjectsView.xaml.cs:line 45\r\n   
at Application.Shared.UI.Panels.FavoriteObjectsView.OnPreviewMouseRightButtonDown(Object sender, MouseButtonEventArgs e) in c:\\XXX\\161wpf\\src\\NisSharedAppFeature\\Panels\\FavoriteObjectsView.xaml.cs:line 52\r\n   
at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)\r\n   
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)\r\n   
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)\r\n   
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)\r\n   
at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)\r\n   
at System.Windows.UIElement.OnPreviewMouseDownThunk(Object sender, MouseButtonEventArgs e)\r\n   
at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)\r\n   
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)\r\n   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)\r\n   
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)\r\n  
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)\r\n   at System.Windows.ContentElement.RaiseTrustedEvent(RoutedEventArgs args)\r\n   
at System.Windows.Input.InputManager.ProcessStagingArea()\r\n   
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)\r\n   
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)\r\n   
at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)\r\n   
at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)\r\n   
at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)\r\n   
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)\r\n   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)\r\n   
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)\r\n   
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)\r\n   
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)\r\n   
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)\r\n   
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)\r\n   
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)\r\n  
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)\r\n   
at System.Windows.Application.RunDispatcher(Object ignore)\r\n   
at System.Windows.Application.RunInternal(Window window)\r\n   
at System.Windows.Application.Run(Window window)\r\n   
at System.Windows.Application.Run()\r\n   at "my application start location"

I can reproduce this only sometimes. My colleague said left click item 1 and right click item 2 produces this every time in certain tree.

char m
  • 7,840
  • 14
  • 68
  • 117
  • A `Run` is no Visual and would need separate handling. does `VisualUpwardSearch(e.Source as DependencyObject);` already fix it for you? Also: to reproduce the click must happen on the Label not the image! – Markus Hütter Jul 12 '16 at 09:44
  • i click on textblock, not image but now i been clicking for minutes and can't reproduce it. so I don't know if this change makes a difference. – char m Jul 12 '16 at 09:49
  • plus changing it to e.Source results the item parent being selected – char m Jul 12 '16 at 10:00

2 Answers2

36

The issues reproduces when you click somewhere in the Label's text. In this case the e.OriginalSource will be a Run object which is a part of internal composition of the Label or TextBox. The Runelement is not inheriting from a Visual class, thus can't be a part of the visual tree, in this case the VisualTreeHelper.GetParent(source); will throw InvalidOperationException.

The easiest solution will be to make each text control (Label in your case) IsHitTestVisible="False", this will exclude these controls from hit testing logic, which means that it will never be the e.OriginalSource of an event, instead it's parent will be picked up and most likely the parent will be a Visual element.

Novitchi S
  • 3,711
  • 1
  • 31
  • 44
  • 1
    thanks! thorough testing will tell if this fixes it. for me it was nearly impossible to reproduce. in 1h of clicking I managed to get the exception 2 times. +1 for explanation. can you tell me about 'most likely part'? so this can happen even if I set IsHitTestVisible="False"? – char m Jul 12 '16 at 10:33
  • so additional check "if (source is Visual)" could be added just to be on the safe side? – char m Jul 12 '16 at 10:39
  • 1
    No, don't bother with the check, it may mask future problems, I mentioned that it will be "most likely a Visual" because there can be cases if you add other text elements to your templates without setting IsHitTestVisible to false. In your case it is 100% safe, you may want to put the IsHitTestVisible only on the root StackPanel of your templates to avoid the problem with other text elements in the template. – Novitchi S Jul 12 '16 at 11:26
  • thanks. my colleague tested and couldn't reproduce anymore. strange bug/feature because it was so difficcult tgo reproduce. – char m Jul 12 '16 at 16:36
  • 3
    I faced the same problem for Run within TextBlock, I set whole TextBlock to IsHitTestVisible="False" problem disappeared. Thanx!!! – Gabriel Jun 07 '18 at 08:07
5

I found that starting the search with the parent worked in my case. My TreeViewItem text consisted of several runs so I couldn't disable hit test.

    private void TextBlock_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        TreeViewItem treeViewItem;
        if (e.OriginalSource is System.Windows.Documents.Run)
            treeViewItem = VisualUpwardSearch(((System.Windows.Documents.Run)e.OriginalSource).Parent as DependencyObject);
        else treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);

        if (treeViewItem != null)
        {
            treeViewItem.IsSelected = true;
            e.Handled = true;
        }
    }
J Straight
  • 53
  • 1
  • 4
  • This answer helped me a lot. The workaround IsHitTestVisible="False" did not fix my problem => it happened that the context menu was not popped up anymore after right-click. – IamJose Jun 08 '22 at 09:42