【问题标题】:Editing a DataGrid in WPF causes a System.NullReferenceException在 WPF 中编辑 DataGrid 会导致 System.NullReferenceException
【发布时间】:2011-07-28 13:25:54
【问题描述】:

我有一个简单的应用程序,它从数据库中读取专辑列表并填充列表框 (AlbumShowCase)。 Whenever a ListBoxItem is selected I update a DataGrid (trackDataGrid) with the list of tracks in that Album (also from the DataBase).

问题是,我可以编辑 DataGrid 中的项目,并且对于所有现有轨道,更改是持久的。但是,如果我尝试添加新曲目,一旦我完成了对行的编辑,我就会得到 System.NullReferenceException。

private TunesDBDataContext db;

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    db = new TunesDBDataContext("TunesDB.sdf");
    var query = from a in db.Albums select new AlbumCase(a);
    AlbumShowCase.ItemsSource = query;
}

private void trackDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    db.SubmitChanges();
}

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var query = from a in db.Albums
                where a.AlbumID == ((AlbumCase)e.AddedItems[0]).Album.AlbumID
                select a.Tracks;

    trackDataGrid.ItemsSource = query;
}

异常发生在我的 ValueConverter 之后:

[ValueConversion(typeof(String), typeof(int))]
public class TimeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int time = (int)value;
        TimeSpan ts = TimeSpan.FromSeconds(time);
        return ts.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // The validation runs before this, so we know that if we got here
        // the data must be valid and won't throw an exception.
        return (int)TimeSpan.Parse((string)value).TotalSeconds;
        // THE EXCEPTION OCCURS AFTER THIS LINE FOR NEW ROWS
    }

TimeConverter 与 TimeConverterRule 配对,确保输入的轨道长度有效,并且据我所知它工作正常。 只有当用户编辑 DataGrid 的最后一行(空行)时才会发生崩溃。这是堆栈跟踪:

System.NullReferenceException was unhandled
Message=Object reference not set to an instance of an object.
Source=PresentationFramework
StackTrace:
    at System.Windows.Data.BindingExpression.IsValidValueForUpdate(Object value, Type sourceType)
    at System.Windows.Data.BindingExpression.ConvertProposedValue(Object value)
    at System.Windows.Data.BindingExpression.ValidateAndConvertProposedValue(Collection1& values)
    at System.Windows.Controls.DataGridHelper.ValidateWithoutUpdate(FrameworkElement element)
    at System.Windows.Controls.DataGridColumn.CommitCellEdit(FrameworkElement editingElement)
    at System.Windows.Controls.DataGridColumn.CommitEdit(FrameworkElement editingElement)
    at System.Windows.Controls.DataGridCell.CommitEdit()
    at System.Windows.Controls.DataGrid.OnExecutedCommitEdit(ExecutedRoutedEventArgs e)
    at System.Windows.Controls.DataGrid.OnExecutedCommitEdit(Object sender, ExecutedRoutedEventArgs e)
    at System.Windows.Input.CommandBinding.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
    at System.Windows.Input.CommandManager.ExecuteCommandBinding(Object sender, ExecutedRoutedEventArgs e, CommandBinding commandBinding)
    at System.Windows.Input.CommandManager.FindCommandBinding(CommandBindingCollection commandBindings, Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
    at System.Windows.Input.CommandManager.FindCommandBinding(Object sender, RoutedEventArgs e, ICommand command, Boolean execute)
    at System.Windows.Input.CommandManager.OnExecuted(Object sender, ExecutedRoutedEventArgs e)
    at System.Windows.UIElement.OnExecutedThunk(Object sender, ExecutedRoutedEventArgs e)
    at System.Windows.Input.ExecutedRoutedEventArgs.InvokeEventHandler(Delegate genericHandler, Object target)
    at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
    at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
    at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
    at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
    at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
    at System.Windows.Input.RoutedCommand.ExecuteImpl(Object parameter, IInputElement target, Boolean userInitiated)
    at System.Windows.Input.RoutedCommand.Execute(Object parameter, IInputElement target)
    at System.Windows.Controls.DataGrid.EndEdit(RoutedCommand command, DataGridCell cellContainer, DataGridEditingUnit editingUnit, Boolean exitEditMode)
    at System.Windows.Controls.DataGrid.CommitAnyEdit()
    at System.Windows.Controls.DataGrid.OnEnterKeyDown(KeyEventArgs e)
    at System.Windows.Controls.DataGrid.OnKeyDown(KeyEventArgs e)
    etc...etc...
    }

【问题讨论】:

  • 我想查看 XAML 声明

标签: c# .net wpf wpfdatagrid


【解决方案1】:

我怀疑这是因为您绑定到 LINQ to SQL 查询的结果。当您编辑该行时,WPF 会尝试将新项目“添加”到您的查询中 - 但它不支持添加。

试试这样的:

var query = from a in db.Albums
            where a.AlbumID == ((AlbumCase)e.AddedItems[0]).Album.AlbumID
            select a.Tracks;

var dataSource = new ObservableCollection<Track>();
foreach (var item in query)
    dataSource.Add(item);

trackDataGrid.ItemsSource = dataSource;

然后您可能需要订阅网格上的事件,以便在添加项目时将其添加到 DbContext。

此外,请确保 AlbumCase 是具有公共无参数构造函数的公共类。那是因为 WPF 会尝试“新建”一个来设置属性。

【讨论】:

  • 这似乎是一个正确的方向,使用我想出的代码:var dataSource = new ObservableCollection&lt;Track&gt;(); foreach (var entity in query) foreach (Track t in entity) { dataSource.Add(t); } trackDataGrid.ItemsSource = dataSource; 并且不再崩溃!我现在必须使更改持久化,但是使用 ObservableCollection 很容易 :) 再次感谢!
【解决方案2】:

使用 .NET Reflector,这是您正在运行的代码(在 System.Windows.Data.BindingExpression 中):

internal override object ConvertProposedValue(object value)
{
    ...
    Type sourcePropertyType = this.Worker.SourcePropertyType;
    IValueConverter dynamicConverter = null;
    CultureInfo culture = base.GetCulture();
    if (this.Converter != null)
    {
        if (!base.UseDefaultValueConverter)
        {
            value = this.Converter.ConvertBack(value, sourcePropertyType, this.ParentBinding.ConverterParameter, culture);
            if (((value != Binding.DoNothing) && (value != DependencyProperty.UnsetValue)) && !this.IsValidValueForUpdate(value, sourcePropertyType))
            {
                dynamicConverter = this.DynamicConverter;
            }
        }

据我所知,“SourcePropertyType”是一个空值(然后它会调用IsValidValueForUpdate)。

所以,这显然是 PresentationFramework 中的一个错误(它应该报告一个很好的错误并优雅地失败),但它发生的原因是,不知何故,您向 WPF 传递了一个为 null 的源属性类型。可能是因为通用的开放类型或匿名类型。

希望这会有所帮助。

为了帮助您诊断,我建议您将 WPF 跟踪转为一个,在 SO 上查看有关此主题的此线程:How to detect broken WPF Data binding?

【讨论】:

  • 感谢您的帮助,我查看了我的 DataBinding 并没有发现任何问题:((从你给我的链接我设置了我的 app.config 来创建 GraveOfBindErrors.txt 并且只有微软的功能区似乎有问题,我的代码没有)。
  • 但是他的查询结果好像不是匿名类型(不是new { … })而且你不能有开放泛型类型的对象。
  • @svick - 是的,可能有些不同。 WPF 堆栈不知何故获得了一个空类型并保持这种状态,而不是抛出和错误。也许是动态程序集?
【解决方案3】:
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
//check value null
if(value==null) return 0;
        return (int)TimeSpan.Parse((string)value).TotalSeconds;

    }

【讨论】:

  • 不幸的是,这并没有成功。该值实际上是有效的(目前在调试器中我可以读取“00:04:25”)。
猜你喜欢
  • 2014-01-10
  • 2013-07-30
  • 2011-03-26
  • 1970-01-01
  • 2012-12-04
  • 2022-01-11
  • 2011-08-23
  • 2011-09-14
相关资源
最近更新 更多