2011-04-03 2 views
0

J'ai une application simple qui lit une liste d'albums à partir d'une base de données et remplit un ListBox (AlbumShowCase). Chaque fois qu'un ListBoxItem est sélectionné, je mets à jour un DataGrid (trackDataGrid) avec la liste des pistes dans cet Album (également à partir de la DataBase).Modification d'un DataGrid dans WPF provoque une exception System.NullReferenceException

Le problème est que je peux éditer les éléments dans le DataGrid, et pour toutes les pistes existantes, les modifications sont persistantes. Mais si j'essaie d'ajouter une nouvelle piste, une fois que j'ai fini d'éditer la ligne, j'obtiens l'exception 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; 
} 

L'exception se produit juste après mon 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 
    } 

Le TimeConverter est jumelé à un TimeConverterRule qui fait que la longueur de la piste entré est valide, et pour tout ce que je sais que ça fonctionne très bien. C'est juste lorsque l'utilisateur édite la dernière ligne (la case vide) du DataGrid que le plantage se produit. Et voici la trace de la pile:

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... 
    } 
+0

Je veux voir la déclaration XAML – ebattulga

Répondre

1

Je soupçonne que c'est parce que vous liez aux résultats d'une requête LINQ to SQL. Lorsque vous modifiez la ligne, WPF tente d'ajouter le nouvel élément à votre requête, mais il ne prend pas en charge l'ajout.

Essayez quelque chose comme ceci:

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; 

Vous devrez peut-être alors de souscrire à des événements sur la grille de sorte que lorsqu'un élément est ajouté, vous l'ajoutez à la DbContext.

De même, assurez-vous que AlbumCase est une classe publique avec un constructeur public sans paramètre. C'est parce que WPF va essayer de "nouveau" jusqu'à définir les propriétés.

+0

Cela semble être un coup dans la bonne direction, en utilisant votre code que j'ai trouvé: \t \t \t 'var dataSource = new ObservableCollection (); \t \t \t foreach (entité var dans la requête) \t \t \t \t foreach (Track t dans l'entité) \t \t \t \t { \t \t \t \t \t dataSource.Add (t); \t \t \t \t} \t \t \t trackDataGrid.ItemsSource = Datasource ' Et plus Crash! Je vais devoir faire le changement persistant maintenant mais c'est facile avec une ObservableCollection :) Merci encore! –

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

    } 
+0

Malheureusement, cela n'a pas fait l'affaire. La valeur est réellement valide (actuellement dans le débogueur je peux lire "00:04:25"). –

1

Piquer avec réflecteur .NET, c'est le code que vous utilisez en (en 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; 
      } 
     } 

D'après ce que je peux comprendre la « SourcePropertyType » est une valeur nulle (et puis il jette l'appel à IsValidValueForUpdate). Donc, ceci est clairement un bug dans PresentationFramework (il devrait signaler une erreur gentille et échouer gracieusement), mais cela arrive parce qu'en quelque sorte, vous passez à WPF un type de propriété source qui est null. Peut-être à cause d'un type ouvert générique ou d'un type anonyme.

Espérons que cela aide.

Pour vous aider à diagnostiquer, je vous suggère de tourner WPF trace un, voir ce fil à ce sujet sur le SO: How to detect broken WPF Data binding?

+0

Merci pour l'aide, j'ai recherché mon DataBinding et ne peux pas trouver quelque chose de mal avec elle :((Du lien que vous m'avez donné je configure mon app.config pour créer le GraveOfBindErrors.txt et seulement le ruban de Microsoft semble avoir des problèmes, rien à partir de mon code) –

+0

Mais le résultat de sa requête ne semble pas être de type anonyme (non 'new {...}') et vous ne pouvez pas avoir un objet de type générique ouvert – svick

+0

@svick - Oui, c'est D'une manière ou d'une autre, la pile WPF obtient un type nul et le conserve comme ceci au lieu de lancer et d'erreur. –

Questions connexes