WPF 在删除 ListBox 中的项并引发 NullReferenceException 时崩溃

WPF Crash when delete items in ListBox and throw NullReferenceException

提问人:Ricky Chen 提问时间:1/18/2023 最后编辑:Ricky Chen 更新时间:1/19/2023 访问量:134

问:

我的应用程序有时会崩溃并引发异常,当我删除 ListBox 中的多个项目时。

我通过 ContextMenu 的 MenuItem 中的 delete 命令删除项目。

我无法重现这个异常,这很奇怪。

日志为:

‧ 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 是:

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

有谁知道这个异常是如何抛出的? 我希望有人能帮助我,谢谢!

更新:我在下面添加转换器代码,绑定是: <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();
    }
}
WPF 列表框 NullReferenceException

评论

0赞 Andy 1/18/2023
请提供足够的代码以尽量减少复制。事实上,我们不知道你绑定的那些东西是什么。我的第一个猜测是该转换器所做的任何事情都是您的问题,但它可能是 selecteditem。
0赞 Andy 1/18/2023
如果注释掉图像,是否仍会出现错误?如果是这样,那么无论转换器做什么,您都需要一种不同的技术来获取您的图像源。
0赞 Ricky Chen 1/19/2023
谢谢,我添加了转换器代码,但我在这里找不到任何问题
0赞 Ricky Chen 1/19/2023
你知道“System.Windows.Controls.VirtualizingStackPanel.GetMaxChildArrangeLength(IList children, Boolean isHorizontal)”是什么意思吗?

答:

1赞 Andy 1/19/2023 #1

什么 System.Windows.Controls.VirtualizingStackPanel.GetMaxChildArrangeLength (IList children, Boolean isHorizontal) 方法:

列表框有一个 VirtualizingStackPanel,用于虚拟化其项。要做到这一点,它需要决定什么适合它。

WPF 有一个度量安排机制,它用来决定所有内容需要什么空间。它查看面板中的每个事物,并询问它想要什么空间,然后查看它有什么限制,并决定它可以作为测量值。然后它安排一切。然后渲染。

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

GetMaxChildArrangeLength 显然决定了它能够在视口中容纳多少个项目。

你有这个错误,因为当涉及到它试图安排的其中一件事时,它会变成空。

您似乎无法重现此错误。

因此,任何修复都必须具有某种程度的推测性。一个猜测。

我的猜测是你的一个或两个转换器太复杂了。也许正在调用的索引转换器实际上是虚拟化的,并且没有容器。

它也可能是您的图像转换器MatToBmp导致的。

我看不到它的来源,但我猜这是一个昂贵的过程,因为你让它异步。

我的建议是让这两者都做更少的工作。让它们更简单或避免它们。

通过将 AlternationCount 设置为高 - 喜欢 10,0000 并绑定到 AlternationIndex,可以更轻松地获取项目索引。添加 1。

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

或者,从视图模型中公开索引 int 并重新计算。

同样,为该图像指定一个固定高度,以减少每个模板中的度量排列。

减少 MatToBmp 正在做的工作。提前构建位图源什么的。

评论

0赞 Ricky Chen 2/1/2023
我想知道我添加这个是否有用:<ListBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation=“Vertical”/> </ItemsPanelTemplate> </ListBox.ItemsPanel>
1赞 Andy 2/1/2023
@Ricky Chen:这是列表框的默认 ItemsPanel 和方向。默认情况下,列表框具有其中一个虚拟化堆栈面板。默认情况下,列表框垂直排列其项。如果要使 itemspanel 成为 stackpanel,则删除虚拟化。我认为你的错误可能会消失。但是,您可能会遇到性能问题,除非您绑定的项数量非常少。因为所有项目都将模板化到 UI 中,而不仅仅是可见的项目。
1赞 Andy 2/1/2023
是的。虚拟化堆栈面板会进行一些计算,并决定哪些内容适合其视口。只有这些项目被模板化到 ui 中。我认为你的代码使这个过程变得非常昂贵。
1赞 Andy 2/2/2023
好点子,想想看,它可能不适用于虚拟化。不过,添加一个 int 属性并在填充集合时递增它将是虚拟化证明。
1赞 Andy 2/2/2023
正确。像这样的直接绑定会快得多。也许更重要的是,它不依赖于查找任何控件等。