【问题标题】:NullReference Exception in PresentationFrameworkPresentationFramework 中的 NullReferenceException
【发布时间】:2016-09-20 12:29:38
【问题描述】:

下面是一个最小的例子,我不可能再减少它了。

我在 ViewModel 中创建了一个实时过滤的 CollectionView,如下所示:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows;

namespace AntiBonto.ViewModel
{
    [Serializable]
    public class Person
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        public string Name { get; set; }
        public override string ToString()
        {
            return Name;
        }

        private int num;
        public int Num
        {
            get { return num; }
            set { num = value; RaisePropertyChanged(); }
        }
    }

    class ObservableCollection2<T> : ObservableCollection<T>
    {
        public ObservableCollection2() : base() { }
        public ObservableCollection2(T[] t) : base(t) { }
        public void AddRange(IEnumerable<T> collection)
        {
            foreach (var i in collection)
            {
                Items.Add(i);
            }
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }

    class MainWindow: ViewModelBase
    {
        public MainWindow() { }
        private ObservableCollection2<Person> people = new ObservableCollection2<Person>();
        public ObservableCollection2<Person> People
        {
            get
            {
                return people;
            }
            set
            {
                people = value;
                RaisePropertyChanged();
            }
        }
        public ICollectionView Team
        {
            get
            {
                CollectionViewSource cvs = new CollectionViewSource { Source = People, IsLiveFilteringRequested = true, LiveFilteringProperties = { "Num" } };
                cvs.View.Filter = p => ((Person)p).Num != 11;
                return cvs.View;
            }
        }

        public ICollectionView Ujoncok
        {
            get
            {
                CollectionViewSource cvs = new CollectionViewSource { Source = People, IsLiveFilteringRequested = true, LiveFilteringProperties = { "Num" } };
                cvs.View.Filter = p => ((Person)p).Num == 11;
                return cvs.View;
            }
        }
    }
}

GUI 有一个按钮,用于修改 People 集合中的 Person 对象:

<Window x:Class="AntiBonto.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:AntiBonto.ViewModel"
        mc:Ignorable="d"
        Title="AntiBonto" Width="1024" Height="768">
    <Window.DataContext>
        <vm:MainWindow/>
    </Window.DataContext>
    <Window.Resources>
        <FrameworkElement x:Key="DataContextProxy" DataContext="{Binding}"/> <!-- workaround, see http://stackoverflow.com/questions/7660967 -->
    </Window.Resources>
    <TabControl>
        <TabItem Header="Tab2">
            <StackPanel>
                <Button Content="Does" Click="Button_Click"/>
                <ContentControl Visibility="Collapsed" Content="{StaticResource DataContextProxy}"/>
                <!-- workaround part 2 -->
                <DataGrid ItemsSource="{Binding Ujoncok}" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridComboBoxColumn Header="Who" ItemsSource="{Binding DataContext.Team, Source={StaticResource DataContextProxy}, Mode=OneWay}"/>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </TabItem>
    </TabControl>
</Window>

我从这样的 XML 文件中加载数据:

using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Xml.Serialization;

namespace AntiBonto
{
    [Serializable]
    public class AppData
    {
        public Person[] Persons;
    }
    public partial class MainWindow : System.Windows.Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }
        private string filepath = "state.xml";
        private AppData AppData
        {
            get { return new AppData { Persons = viewModel.People.ToArray()}; }
            set { viewModel.People.AddRange(value.Persons);}
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            var xs = new XmlSerializer(typeof(AppData));
            if (File.Exists(filepath))
            {
                using (var file = new StreamReader(filepath))
                {
                    AppData = (AppData)xs.Deserialize(file);
                }
            }
        }     

        private ViewModel.MainWindow viewModel { get { return (ViewModel.MainWindow)DataContext; } }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Person p = viewModel.People.First(q => q.Name == "Ferencz Katalin");
            if (p.Num == 11)
                p.Num = 0;
            else
                p.Num= 11;
        }
    }
}

XML 文件是这样的:

<?xml version="1.0" encoding="utf-8"?>
<AppData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Persons>
    <Person>
      <Name>Person1</Name>
      <Num>0</Num>
    </Person>
    <Person>
      <Name>Person2</Name>
      <Num>0</Num>
    </Person>
  </Persons>
</AppData>

当我单击按钮一次或两次时,我得到一个NullReference 异常。没有内在的例外。我的代码中没有出现异常,而是在框架代码中出现了异常,因此它没有显示源代码,我无法找出哪个对象为空以及异常来自何处。我没有设法设置“进入 .NET 源”,它仍然告诉我没有可用的源。

这是一个堆栈跟踪:

在 System.Windows.Data.ListCollectionView.RestoreLiveShaping() 在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(委托 回调,对象参数,Int32 numArgs)在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象源, 委托回调、对象 args、Int32 numArgs、委托 catchHandler) 在 System.Windows.Threading.DispatcherOperation.InvokeImpl() 在 System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(对象 州)在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态)在 MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext、ContextCallback 回调、对象状态)在 System.Windows.Threading.DispatcherOperation.Invoke() 在 System.Windows.Threading.Dispatcher.ProcessQueue() 在 System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd,Int32 味精,IntPtr wParam,IntPtr lParam,布尔值&处理)在 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd,Int32 msg,IntPtr wParam, IntPtr lParam,布尔值&处理)在 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) 在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(委托 回调,对象参数,Int32 numArgs)在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(对象源, 委托回调、对象 args、Int32 numArgs、委托 catchHandler) 在 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority 优先级、TimeSpan 超时、委托方法、对象参数、Int32 numArgs) 在 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 味精,IntPtr wParam,IntPtr lParam)在 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) 在 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame 帧)在 System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame 框架) 在 System.Windows.Application.RunDispatcher(对象忽略) System.Windows.Application.RunInternal(窗口窗口)在 System.Windows.Application.Run(窗口窗口)在 System.Windows.Application.Run() 在 AntiBonto.App.Main() 中 D:\Marci\Programozás\AntiBonto\AntiBonto\obj\Debug\App.g.cs:line 0 at System.AppDomain._nExecuteAssembly(RuntimeAssembly 程序集,字符串 [] args) 在 System.AppDomain.ExecuteAssembly(String assemblyFile, 证据 assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ThreadHelper.ThreadStart_Context(对象状态)在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态)在 System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

  • @RenéVogt 不,不是。
  • 我不会将其称为重复,因为它与另一个问题具有相同的非常通用的异常。
  • @Joe:它可能不是规范的“什么是 NullReferenceException...”问题的完全重复,但这个问题仍然表现出缺乏研究,并且未能提供良好的@ 987654321@ 可靠地重现问题。如果问题只能使用第三方库重现,那么寻求帮助的正确位置是该库的作者。如果不需要该库,那么这里可能是一个问题合适,但问题需要包括一个可重现的例子。
  • @Peter Duniho 几乎所有关于 Stackoverflow 的问题都未能提供最小、完整和可验证的示例(有时不可能)。这个人做了他的研究,他在互联网上找到了关于这个问题的唯一其他信息(这是我的问题,它面临同样的问题,人们说它是重复的)。是的,使用外部库并不理想,但它不会使问题无效。人们不喜欢这个问题,因为它有一个简单的例外,许多新编码人员都面临,并仓促下结论。
  • 我重新打开了这个问题,因为这是关于 .NET BCL 中的 NRE(显然是 ListCollectionView 实现中的一个错误),而不是 OP 的代码。因此,欺骗中描述的解决方案不适用。

标签: c# .net wpf


【解决方案1】:

我不知道为什么,但这修复了错误:

public ICollectionView Team
{
    get
    {
        CollectionViewSource cvs = new CollectionViewSource { Source = People, IsLiveFilteringRequested = true, LiveFilteringProperties = { "Num" } };
        cvs.View.Filter = p => ((Person)p).Num != 11;
        cvs.View.CollectionChanged += EmptyEventHandler;
        return cvs.View;
    }
}
private void EmptyEventHandler(object sender, NotifyCollectionChangedEventArgs e) { }

我试图调试异常发生的位置,并且我想在集合更改时设置一个断点。订阅事件使异常消失。

【讨论】:

【解决方案2】:

我花了很长时间尝试调试System.Windows,但没有成功,欢迎您尝试。

就至少可以发挥作用的创可贴解决方法而言,from my question 我发现new CollectionViewSource 会导致问题,而CollectionViewSource.GetDefaultView 不会。

你提到这有两个问题:

1) 我一直在修改基础集合,需要刷新过滤器

您可以通过使用 ObservableCollections 并侦听版本,然后更新过滤的实例来解决此问题。

所以每次你需要一个新的 CollectionViewSource 时,创建一个新的 ObservableCollection 并跟踪它,为原始 ObservableCollection 添加一个监听器并将更新推送到 CollectionViewSource 的版本。

优雅?不,功能性?是的。

2) 我正在使用 GetDefaultView 不提供的实时整形。

我一直都可以使用 GetDefaultView,你能说明你在哪里创建视图吗?

我注意到的一个相似之处是我正在使用 ObservableCollection 的扩展。如果您只使用标准的 ObservableCollection,您会遇到问题吗?

【讨论】:

  • 稍后我会添加我自己的答案。为每个 CollectionViews 添加一个空的 CollectionChanged 事件处理程序解决了这个问题,我不知道为什么。
猜你喜欢
  • 1970-01-01
  • 2016-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 1970-01-01
  • 2020-11-10
相关资源
最近更新 更多