Skip to content

TreeView can result in NRE #530

@chucker

Description

@chucker

Describe the bug
When dragging from a TreeView, I'm getting a NullReferenceException, ultimately in TypeUtilities.CreateDynamicallyTypedList, sourceObjects.Select(o => o.GetType().

To Reproduce
This is tricky. My tree is just:

       <TreeView
           dd:DragDrop.DropHandler="{Binding}"
           dd:DragDrop.IsDragSource="True"
           dd:DragDrop.IsDropTarget="True"
           dd:DragDrop.UseDefaultDragAdorner="True"
           ItemsSource="{Binding CollectionView}">
           <TreeView.ItemTemplate>
               <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                   <TreeViewItem>
                       <TreeViewItem.ContextMenu>
                           … probably not relevant
                       </TreeViewItem.ContextMenu>

                       <TreeViewItem.Header>
                           … probably not relevant
                           </StackPanel>
                       </TreeViewItem.Header>
                   </TreeViewItem>
               </HierarchicalDataTemplate>
           </TreeView.ItemTemplate>
       </TreeView>

(One thing of note is that it is styled via https://github.com/lepoco/wpfui. That may contribute to the bug.)

Now, all I do is start drag from a tree node. This throws.

Expected behavior
It shouldn't throw, especially not in unrecoverable way.

Desktop (please complete the following information):

  • Windows 11 26220.8138
  • .NET Framework 4.7.2

Additional context
This code throws:

    public static class TypeUtilities
    {
        public static IEnumerable CreateDynamicallyTypedList(IEnumerable source)
        {
            var sourceObjects = source.Cast<object>().ToArray();
            var type = GetCommonBaseClass(sourceObjects.Select(o => o.GetType()).Distinct().ToArray());

Specifically, sourceObjects is an array with one item, but that item is null; therefore, GetType() on it throws.

I've tracked down where that item comes from; it's in DragInfo:

                    // Some controls (I'm looking at you TreeView!) haven't updated their
                    // SelectedItem by this point. Check to see if there 1 or less item in
                    // the SourceItems collection, and if so, override the control's SelectedItems with the clicked item.
                    //
                    // The control has still the old selected items at the mouse down event, so we should check this and give only the real selected item to the user.
                    if (selectedItems.Count <= 1 || this.SourceItem != null && !selectedItems.Contains(this.SourceItem))
                    {
                        this.SourceItems = Enumerable.Repeat(this.SourceItem, 1);
                    }

This is true, because selectedItems is empty. However, this.SourceItem is null, so it puts null in a single-item collection.

SourceItem is never set because itemParent is null:

                    var itemParent = ItemsControl.ItemsControlFromItemContainer(item);

                    if (itemParent != null)

I'm not quite sure why that is. itemsControl is not null; it correctly points to the TreeView. And item is, correctly, a TreeViewItem.

I'm also unsure what this code does. item is set, and it seems in this case, the code should just fall back to putting item.DataContext in SourceItems, which is already correctly set.

Moreover, CreateDynamicallyTypedList should probably filter for null before calling GetType(). Or, the try/catch block should encompass the call to GetCommonBaseClass, too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions