dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.02k stars 1.16k forks source link

TabControl bug with sorted DataGrid #2662

Open Markz878 opened 4 years ago

Markz878 commented 4 years ago

I think the TabControl has a bug-like behavior with sorted DataGrids. If you have a TabControl using a DataTemplate that has DataGrids in it, and you sort a DataGrid column and try to edit a cell in that column, and then try to change tabs in the TabControl, the program crashes with a framework exception that seems impossible to catch beforehand.

I don't believe that this is a desired behavior. The DataGrid should just stop the sorting when tabs are changed, I think the easiest solution is to just remove the throw in the source code.

I have created a minimal repro here: https://github.com/Markz878/TabDataGridError

If anyone knows of another solution to this problem, please let me know.

anjali-wpf commented 1 year ago

@Markz878 I am not able to run the sample app provided by you, could you please check and provide a running sample.

Markz878 commented 1 year ago

I updated the project to target .NET 7, try it now.

anjali-wpf commented 1 year ago

@Markz878 It looks like this issue is not longer reproducible in .NET6, also on .NET 8, please check the attached video.

https://github.com/dotnet/wpf/assets/85492722/13090e91-21e7-4e07-934c-ca0d52c77d07

Could you please check.

miloush commented 1 year ago

@anjali-wpf almost there, at 0:23 when you edit the cell, switch to the other tab without sorting/ending the edit.

Markz878 commented 1 year ago

Hi, I tested it during the update and it still crashes. You need to double click a cell after you have started the sorting. So first sort the column, then start editing a cell, and without submitting the cell edit change the tab.

Markz878 commented 1 year ago

I don't have any video recording software on my machine and I'd rather not download it just for this. I believe that you can reproduce this with these steps:

  1. Sort the "Tab1"'s DataGrid by pressing "Value".
  2. Start editing the value by double-clicking the cell with 0 in it (don't submit the edit or click anywhere else).
  3. Press the second tab "Tab2".
  4. The program crashes with error "System.InvalidOperationException: ''Sorting' is not allowed during an AddNew or EditItem transaction.'"
miloush commented 1 year ago

For the record, the callstack is

   at System.Windows.Data.ListCollectionView.SortDescriptionsChanged(Object sender, NotifyCollectionChangedEventArgs e) in System.Windows.Data\ListCollectionView.cs:line 2275
   at System.ComponentModel.SortDescriptionCollection.OnCollectionChanged(NotifyCollectionChangedAction action) in System.ComponentModel\SortDescriptionCollection.cs:line 108
   at System.ComponentModel.SortDescriptionCollection.ClearItems() in System.ComponentModel\SortDescriptionCollection.cs:line 61
   at System.Collections.ObjectModel.Collection`1.Clear()
   at System.Windows.Controls.ItemCollection.CloneList(IList clone, IList master) in System.Windows.Controls\ItemCollection.cs:line 1832
   at System.Windows.Controls.ItemCollection.SynchronizeCollections[T](NotifyCollectionChangedEventArgs e, Collection`1 origin, Collection`1 clone) in System.Windows.Controls\ItemCollection.cs:line 1817
   at System.Windows.Controls.ItemCollection.SortDescriptionsChanged(Object sender, NotifyCollectionChangedEventArgs e) in System.Windows.Controls\ItemCollection.cs:line 1611
   at System.ComponentModel.SortDescriptionCollection.OnCollectionChanged(NotifyCollectionChangedAction action) in System.ComponentModel\SortDescriptionCollection.cs:line 108
   at System.ComponentModel.SortDescriptionCollection.ClearItems() in System.ComponentModel\SortDescriptionCollection.cs:line 61
   at System.Collections.ObjectModel.Collection`1.Clear()
   at System.Windows.Controls.DataGrid.ClearSortDescriptionsOnItemsSourceChange() in System.Windows.Controls\DataGrid.cs:line 6017
   at System.Windows.Controls.DataGrid.OnCoerceItemsSourceProperty(DependencyObject d, Object baseValue) in System.Windows.Controls\DataGrid.cs:line 6032
   at System.Windows.DependencyObject.ProcessCoerceValue(DependencyProperty dp, PropertyMetadata metadata, EntryIndex& entryIndex, Int32& targetIndex, EffectiveValueEntry& newEntry, EffectiveValueEntry& oldEntry, Object& oldValue, Object baseValue, Object controlValue, CoerceValueCallback coerceValueCallback, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, Boolean skipBaseValueChecks) in System.Windows\DependencyObject.cs:line 1156
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) in System.Windows\DependencyObject.cs:line 1086
   at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue) in System.Windows\DependencyObject.cs:line 992
   at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp /* ItemsSource */) in System.Windows\DependencyObject.cs:line 981
   at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange) in System.Windows.Data\BindingExpressionBase.cs:line 1271
   at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange) in System.Windows.Data\BindingExpression.cs:line 1064
   at System.Windows.Data.BindingExpression.Activate(Object item) in System.Windows.Data\BindingExpression.cs:line 786
   at System.Windows.Data.BindingExpression.OnDataContextChanged(DependencyObject contextElement) in System.Windows.Data\BindingExpression.cs:line 1718
   at System.Windows.Data.BindingExpression.HandlePropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) in System.Windows.Data\BindingExpression.cs:line 1896
   at System.Windows.Data.BindingExpressionBase.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) in System.Windows.Data\BindingExpressionBase.cs:line 799
   at System.Windows.Data.BindingExpression.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) in System.Windows.Data\BindingExpression.cs:line 352
   at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs) in System.Windows\DependentList.cs:line 49
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args /* DataContext */) in System.Windows\DependencyObject.cs:line 1206
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) in System.Windows\DependencyObject.cs:line 1132
   at System.Windows.TreeWalkHelper.OnInheritablePropertyChanged(DependencyObject d, InheritablePropertyChangeInfo info, Boolean visitedViaVisualTree) in System.Windows\TreeWalkHelper.cs:line 443
   at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree) in System.Windows\DescendentsWalker.cs:line 318
   at System.Windows.DescendentsWalker`1.VisitNode(FrameworkElement fe, Boolean visitedViaVisualTree) in System.Windows\DescendentsWalker.cs:line 291
   at System.Windows.DescendentsWalker`1.VisitNode(DependencyObject d, Boolean visitedViaVisualTree) in System.Windows\DescendentsWalker.cs:line 311
   at System.Windows.DescendentsWalker`1.WalkLogicalChildren(FrameworkElement feParent, FrameworkContentElement fceParent, IEnumerator logicalChildren) in System.Windows\DescendentsWalker.cs:line 158
   at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) in System.Windows\DescendentsWalker.cs:line 238
   at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) in System.Windows\DescendentsWalker.cs:line 70
   at System.Windows.DescendentsWalker`1._VisitNode(DependencyObject d, Boolean visitedViaVisualTree) in System.Windows\DescendentsWalker.cs:line 322
   at System.Windows.DescendentsWalker`1.VisitNode(FrameworkElement fe, Boolean visitedViaVisualTree) in System.Windows\DescendentsWalker.cs:line 291
   at System.Windows.DescendentsWalker`1.VisitNode(DependencyObject d, Boolean visitedViaVisualTree) in System.Windows\DescendentsWalker.cs:line 311
   at System.Windows.DescendentsWalker`1.WalkFrameworkElementLogicalThenVisualChildren(FrameworkElement feParent, Boolean hasLogicalChildren) in System.Windows\DescendentsWalker.cs:line 242
   at System.Windows.DescendentsWalker`1.IterateChildren(DependencyObject d) in System.Windows\DescendentsWalker.cs:line 70
   at System.Windows.DescendentsWalker`1.StartWalk(DependencyObject startNode, Boolean skipStartNode) in System.Windows\DescendentsWalker.cs:line 50
   at System.Windows.TreeWalkHelper.InvalidateOnInheritablePropertyChange(FrameworkElement fe, FrameworkContentElement fce, InheritablePropertyChangeInfo info, Boolean skipStartNode) in System.Windows\TreeWalkHelper.cs:line 394
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) in System.Windows\FrameworkElement.cs:line 2593
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) in System.Windows\DependencyObject.cs:line 1183
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) in System.Windows\DependencyObject.cs:line 1132
   at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) in System.Windows\DependencyObject.cs:line 809
   at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) in System.Windows\DependencyObject.cs:line 602
   at System.Windows.FrameworkElement.set_DataContext(Object value) in System.Windows\FrameworkElement.cs:line 558
   at System.Windows.Controls.ContentPresenter.OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) in System.Windows.Controls\ContentPresenter.cs:line 552
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) in System.Windows\DependencyObject.cs:line 1351
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) in System.Windows\FrameworkElement.cs:line 2527
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) in System.Windows\DependencyObject.cs:line 1183
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) in System.Windows\DependencyObject.cs:line 1132
   at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp, Boolean preserveCurrentValue) in System.Windows\DependencyObject.cs:line 992
   at System.Windows.StyleHelper.InvalidateDependents(Style ownerStyle, FrameworkTemplate frameworkTemplate, DependencyObject container, DependencyProperty dp, FrugalStructList`1& dependents, Boolean invalidateOnlyContainer) in System.Windows\StyleHelper.cs:line 1986
   at System.Windows.StyleHelper.OnTriggerSourcePropertyInvalidated(Style ownerStyle, FrameworkTemplate frameworkTemplate, DependencyObject container, DependencyProperty dp, DependencyPropertyChangedEventArgs changedArgs, Boolean invalidateOnlyContainer, FrugalStructList`1& triggerSourceRecordFromChildIndex, FrugalMap& propertyTriggersWithActions, Int32 sourceChildIndex) in System.Windows\StyleHelper.cs:line 1966
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) in System.Windows\FrameworkElement.cs:line 2555
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) in System.Windows\DependencyObject.cs:line 1183
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) in System.Windows\DependencyObject.cs:line 1132
   at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) in System.Windows\DependencyObject.cs:line 809
   at System.Windows.DependencyObject.SetValue(DependencyPropertyKey key, Object value) in System.Windows\DependencyObject.cs:line 681
   at System.Windows.Controls.TabControl.set_SelectedContent(Object value) in System.Windows.Controls\TabControl.cs:line 90
   at System.Windows.Controls.TabControl.UpdateSelectedContent() in System.Windows.Controls\TabControl.cs:line 431
   at System.Windows.Controls.TabControl.OnSelectionChanged(SelectionChangedEventArgs e) in 
...
anjali-wpf commented 1 year ago

@Markz878 @miloush Thanks, I am now able to reproduce the problem now. I will cross check with the specs to see if this is an implementation issue.