WPF 数据表格无法退出编辑模式

7
我有一个显示多行列数据的DataGrid,每个数据都绑定了自己的属性。DataGrid不在TabControl上,但每当它所在的窗口关闭并重新打开时,如果DataGrid的某些单元格存在验证问题,则会出现错误消息:“'DeferRefresh' is not allowed during an AddNew or EditItem transaction.”。
例如:绑定到DataGrid的单元格的属性是double类型,用户输入“hello”,此时WPF会在单元格周围自动显示红色边框。现在,如果用户关闭窗口并重新打开它,则会发生错误。
我知道为什么会出现这个错误,因为单元格没有离开“编辑模式”。
如何解决这个错误?
其他注意事项:
  • 我已经查看了这个问题,但那与一个TabControl上的DataGrid有关(而我的不是)。
  • 我已经尝试限制用户的输入,以便如果Cell需要一个double,那么只接受一个double,但问题在于没有好的方法来限制用户可以插入的小数点(".")的数量。我通过PreviewTextInput控制用户输入,并只允许0-9和"。"。

异常详情(如果有帮助的话)

System.InvalidOperationException was unhandled
  Message='DeferRefresh' is not allowed during an AddNew or EditItem transaction.
  Source=PresentationFramework
  StackTrace:
       at System.Windows.Data.CollectionView.DeferRefresh()
       at System.Windows.Controls.ItemCollection.SetCollectionView(CollectionView view)
       at System.Windows.Controls.ItemsControl.OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
       at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
       at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
       at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
       at System.Windows.Data.BindingExpression.Activate(Object item)
       at System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt)
       at System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
       at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
       at MS.Internal.Data.DataBindEngine.Run(Object arg)
       at MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
       at System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
       at System.Windows.ContextLayoutManager.UpdateLayout()
       at System.Windows.Interop.HwndSource.SetLayoutSize()
       at System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
       at System.Windows.Window.SetRootVisualAndUpdateSTC()
       at System.Windows.Window.SetupInitialState(Double requestedTop, Double requestedLeft, Double requestedWidth, Double requestedHeight)
       at System.Windows.Window.CreateSourceWindow(Boolean duringShow)
       at System.Windows.Window.ShowHelper(Object booleanBox)
       at REACT.ViewModel.ReceiverListViewModel.ShowWindow(String name) in C:\Users\jcarroll42\Documents\Sandbox\REACT\SW\Source\ViewModel\ReceiverListViewModel.cs:line 238
       at REACT.Commands.ShowWindowCommand.Execute(Object parameter) in C:\Users\jcarroll42\Documents\Sandbox\REACT\SW\Source\Commands.cs:line 137
       at MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(ICommandSource commandSource, Boolean userInitiated)
       at System.Windows.Controls.Button.OnClick()
       at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
       at System.Windows.Input.InputManager.ProcessStagingArea()
       at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
       at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
       at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
       at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
       at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
       at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       at System.Windows.Application.RunInternal(Window window)
       at System.Windows.Application.Run()
       at REACT.App.Main() in C:\Users\jcarroll42\Documents\Sandbox\REACT\SW\Source\obj\x86\Debug\App.g.cs:line 0
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

我很乐意提供更多的信息,如果需要的话。

你是每次都使用新的窗口/数据网格实例吗? - Liju
1
我通过在窗口关闭事件中调用buttonBlah.Focus()来解决了一个类似的问题,这会强制触发单元格的LostFocus事件。我知道这不是很好看...另一种解决方案是只允许使用"保存"和"取消"按钮关闭窗口。 - Jack
@JordanCarroll 尝试处理 Window_Closing 事件并清除 datagrid 数据源。但由于每次都是一个新的网格实例,所以这个问题根本不应该发生。 - Liju
问题显然是一个众所周知的错误。问题在于,如果文本与属性的“类型”不匹配,则“单元格”不会退出编辑模式(如果存在无效文本)。这很有道理,但这是一个错误。问题也不是它没有失去焦点。显然,在4.5中已经修复了这个问题,但对我来说没有任何好处。到目前为止,我认为没有什么好的解决方法或变通方法。 - Jordan Carroll
我在使用.NET 4.7.1时遇到了同样的问题,而且现在比一开始更糟糕了。现在它永远不会取消或提交编辑模式,即使没有验证错误。我看到单元格触发了LostFocus事件,但似乎为时已晚,只有@Jack提到的解决方法可行。 - Anateus
3个回答

12

当您需要时,可以尝试强制数据网格取消编辑:

myDatagrid.CommitEdit(); 
myDatagrid.CancelEdit();

这对我有用,当我重新排序行时遇到了类似的问题。


1
你把那段代码放在哪里?我试过将其放在关闭窗口的按钮引发的事件上,但对我来说并没有解决问题。 - FrancescoDS
1
@FrancescoDS 嘿,我也遇到了类似的问题,错误是由于 dataGrid.ItemsSource = data; 这一行触发的。我把它放在导致错误的那一行前面,这样就解决了 - 对我来说,这是由于在 datagrid 中误双击了一个单元格造成的(双击让程序认为用户想编辑单元格的内容,但这并不是用户的意图)。 - ajivani

5

这是一个在.NET Framework 4.5+中已经修复的错误。不幸的是,对于那些被困在之前版本中的人来说,这个错误仍然是一个问题。我找到的最好的解决方案是简单地将DataGrid绑定到类型为string属性。这样,就可以完全控制用户输入。通过完全控制,用户不应该能够触发任何错误,DataGrid也不会崩溃程序。


我遇到了与.NET 4.5.1完全相同的问题。 - l33t
@l33t 修好了吗?我也在使用.NET 4.5,但仍然遇到这个问题。 - Mark A. Donohoe
4
@Jordan,你能否提供显示这个问题何时被修复/解决的文档链接?是在4.5还是4.5.1、4.5.2等版本中解决的?我在搜索中找不到任何关于它被解决甚至被修复的信息。 - Mark A. Donohoe
1
@MarquelV,目前还没有修复 :( - l33t
3
即使使用.NET 4.6,我仍然遇到了同样的问题。 - rabejens

1

我遇到了这个问题,使用.NET 4.5.1,这是唯一对我有用的方法。

    ListCollectionView lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(MyDatagrid.ItemsSource);


                if (lcv.IsAddingNew)
                    lcv.CommitNew();
                if (lcv.IsEditingItem)
                    lcv.CommitEdit();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接