简体   繁体   中英

WPF Crash when delete items in ListBox and throw NullReferenceException

all

My application sometimes crashes and throws an exception When I delete multiple items in ListBox.

I delete items through the delete command in MenuItem of ContextMenu.

I can't reproduce this exception, it's very strange.

The log is:

‧ System.NullReferenceException: Object reference not set to an instance of an object.    
  at System.Windows.Controls.VirtualizingStackPanel.GetMaxChildArrangeLength(IList children, Boolean isHorizontal) 
  at System.Windows.Controls.VirtualizingStackPanel.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at MS.Internal.Helper.ArrangeElementWithSingleChild(UIElement element, Size arrangeSize)    
  at System.Windows.Controls.ItemsPresenter.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.ScrollContentPresenter.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Grid.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Control.ArrangeOverride(Size arrangeBounds)    
  at System.Windows.Controls.ScrollViewer.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Border.ArrangeOverride(Size finalSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Control.ArrangeOverride(Size arrangeBounds)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at MS.Internal.Helper.ArrangeElementWithSingleChild(UIElement element, Size arrangeSize)    
  at System.Windows.Controls.ContentPresenter.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Border.ArrangeOverride(Size finalSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Grid.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Control.ArrangeOverride(Size arrangeBounds)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Grid.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.Controls.Grid.ArrangeOverride(Size arrangeSize)    
  at System.Windows.FrameworkElement.ArrangeCore(Rect finalRect)    
  at System.Windows.UIElement.Arrange(Rect finalRect)    
  at System.Windows.ContextLayoutManager.UpdateLayout()    
  at System.Windows.Interop.HwndSource.Process_WM_SIZE(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam)    
  at System.Windows.Interop.HwndSource.LayoutFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)    
  at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)    
  at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)    
  at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)    
  at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

Xaml is:

<ListBox ItemsSource="{Binding ImgList}" SelectedItem="{Binding SelectedImg}" Style="{StaticResource ListBoxScroll}" SelectionMode="Extended" Background="#FF353535" HorizontalContentAlignment="Stretch" SelectionChanged="ListBox_SelectionChanged" BorderBrush="{x:Null}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0">
    <ListBox.ContextMenu>
        <ContextMenu>                            
           <MenuItem Header="Delete File" Command="{Binding DeleteImageFileCommand}"/>
        </ContextMenu>
    </ListBox.ContextMenu>
    <ListBox.Resources>
        <DataTemplate x:Key="ItemTemplate">
             <Border Height="117" Width="208" BorderBrush="#FF979797" BorderThickness="1" Margin="21,0,0,18">
                 <Grid>
                    <Label x:Name="Lab" Width="40" Height="20" Grid.Column="0" Content="{Binding Converter={StaticResource ItemToIndex}, Mode=OneWay, Path=., RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" FontSize="14"  HorizontalContentAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Top" Padding="0" Panel.ZIndex="1" Background="White" BorderBrush="#FF353535" BorderThickness="1" Foreground="#FF011627" Margin="-1,-1,0,0"/>
                    <Image Grid.Column="1" Width="208" Source="{Binding SmallImg, Converter={StaticResource MatToBmp}, IsAsync=True}" Stretch="Uniform"/>
                </Grid>
            </Border>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>

Does anyone know how this exception is being thrown? I hope someone can help me, Thanks!

Update: I add the converter code below, and binding is: <local:FilterIndexConverter x:Key="ItemToIndex"/>

public class FilterIndexConverter : IValueConverter
{
    public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
    {
        ListBoxItem item = (ListBoxItem)value;
        ListBox listBox = ItemsControl.ItemsControlFromItemContainer(item) as ListBox;
        int index = (listBox.ItemsSource as RangeObservableCollection<ImageInformation>).IndexOf((value as ListBoxItem).Content as ImageInformation);
        return index + 1;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

What System.Windows.Controls.VirtualizingStackPanel.GetMaxChildArrangeLength(IList children, Boolean isHorizontal) Means:

The listbox has a VirtualizingStackPanel which is used to virtualise it's items. To do this it needs to decide what's going to fit in it.

WPF has a measure arrange mechanism which it uses to decide on what space everything needs. It looks at each thing in say a panel and asks what space it would like, then it looks at what constraints there are on it and decides what it can have as a measurement. It then arranges everything. Then renders.

https://learn.microsoft.com/en-us/do.net/desktop/wpf/advanced/layout?view.netframeworkdesktop-4.8#LayoutSystem_Overview

GetMaxChildArrangeLength is pretty obviously deciding how many items it will be able to fit in the viewport.

You have this error because when it comes to one of those things it's trying to arrange, it gets null.

You do not seem to be able to reproduce this error.

Any fix is therefore going to have to be somewhat speculative. A guess.

My guess is one or both of your converters are too complicated. Maybe that index converter is being called on something is actually virtualised and doesn't have a container.

It could also / alternatively be your image converter MatToBmp causing this.

I can't see the source to that but I'm guessing it's an expensive process because you made it async.

My suggestion is to make both these do less work. Make them simpler or obviate them.

You can get item index much easier by setting AlternationCount high - to like 10,0000 and bind to AlternationIndex. Add 1.

    {Binding
     RelativeSource={RelativeSource Mode=TemplatedParent}, 
     Path=(ItemsControl.AlternationIndex)}

Or alternatively expose an index int from your viewmodel and recalculate.

Similarly, give that image a fixed height to cut down on measure arrange in each template.

Reduce the work MatToBmp is doing. Build bitmapsource in advance or something.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM