[英]Editing a DataGrid in WPF causes a System.NullReferenceException
I have a simple application that reads a list of Albums from a DataBase and fills a ListBox (AlbumShowCase). 我有一个简单的应用程序,从数据库中读取相册列表并填充ListBox(AlbumShowCase)。 Whenever a ListBoxItem is selected I update a DataGrid (trackDataGrid) with the list of tracks in that Album (also from the DataBase).
每当选择ListBoxItem时,我都会使用该Album中的轨道列表(也来自DataBase)更新DataGrid(trackDataGrid)。
The problem is, I can edit the items in the DataGrid, and for all the existing tracks, changes are persistent. 问题是,我可以编辑DataGrid中的项目,对于所有现有轨道,更改是持久的。 But if I try to add a new track, once I finish editing the row I get the System.NullReferenceException.
但是如果我尝试添加一个新的轨道,一旦我完成了对该行的编辑,我就会得到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;
}
The Exception occurs right after my ValueConverter: 异常发生在我的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
}
The TimeConverter is paired with a TimeConverterRule that makes sure the track length entered is valid, and for all I know it's working fine. TimeConverter与TimeConverterRule配对,确保输入的轨道长度有效,并且据我所知,它工作正常。 It's just when the user edits the LAST row (the empty one) of the DataGrid that the crash occurs.
只是当用户编辑崩溃发生的DataGrid的最后一行(空行)时。 And here's the stack trace:
这是堆栈跟踪:
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...
}
My suspicion is that this is because you are binding to the results of a LINQ to SQL query. 我怀疑这是因为你绑定了LINQ to SQL查询的结果。 When you edit the row, WPF tries to "Add" the new item to your query - but it doesn't support adding.
编辑行时,WPF会尝试将新项“添加”到您的查询中 - 但它不支持添加。
Try something like this: 尝试这样的事情:
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;
You might then need to subscribe to events on the grid so that when an item is added, you add it to the DbContext. 然后,您可能需要订阅网格上的事件,以便在添加项目时将其添加到DbContext。
Also, make sure AlbumCase is a public class with a public, parameterless constructor. 另外,确保AlbumCase是一个带有公共无参数构造函数的公共类。 That's because WPF will try to "new" one up to set properties on.
那是因为WPF会尝试“新”一个来设置属性。
Poking with .NET Reflector, this is the code you're running into (in System.Windows.Data.BindingExpression): 使用.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;
}
}
From what I can understand the "SourcePropertyType" is a null value (and then it throws in the call to IsValidValueForUpdate
). 根据我的理解,“SourcePropertyType”是一个空值(然后它会抛出对
IsValidValueForUpdate
的调用)。
So, this is clearly a bug in PresentationFramework (it sould report a nice error and fail gracefully), but it happens because somehow, you pass to WPF a source property type that is null. 所以,这显然是PresentationFramework中的一个错误(它可以报告一个很好的错误并且优雅地失败),但是它发生了,因为不知何故,你向WPF传递了一个null的源属性类型。 Maybe because of a generic open type or an anonymouse type.
也许是因为通用开放类型或匿名类型。
Hope this helps. 希望这可以帮助。
To help you diagnose, I suggest you turn WPF traces one, see this thread on this subject on SO: How to detect broken WPF Data binding? 为了帮助您诊断,我建议您将WPF跟踪转为一个,在SO上看到关于此主题的这个主题: 如何检测损坏的WPF数据绑定?
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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.