我有一个tabControl。在其中一个tabitem我有一个usercontrol,它包含一个具有CanUserAddRows =“True”的数据网格。用户可以在列中键入数据并按下输入创建的新行(我假设datagrid执行此CanzserAddRows =“True”)。问题是当我输入数据并更改标签时,我得到此异常“在'Addnew'开始的交易期间不允许使用”WPF datagrid'newitemplaceholderposition'“
有什么建议可以避免吗?
我试图将dg.CommitEdit()放在usercontrol.unloaded()上。我没有得到例外,但我没有得到新的行。
我遇到了同样的问题......这里有一些片段描述了我是如何解决它的。请注意,在我的情况下,我想拒绝更改以避免错误。如果您想提交更改,这可能会引导您朝着正确的方向前进。
1a)使用datagrid上的InitializingNewItem事件来捕获添加行。
private void mydatagrid_InitializingNewItem(object sender, InitializingNewItemEventArgs e)
{
_viewmodel.NewRowDefaults((DataRowView)e.NewItem);
}
1b)在这种情况下,我在视图模型中调用一个方法来填充行默认值并保存对该行的引用。
private DataRowView _drvAddingRow { get; set; }
public void NewRowDefaults(DataRowView drv)
{
_drvAddingRow = drv;
...
}
2)然后,当您需要拒绝更改时(在通知属性更改或任何情况之前),请在捕获的数据视图上使用CancelEdit方法。
_drvAddingRow.CancelEdit();
我刚遇到同样的问题。找到两种可能的解决方法:
1 /触发DataGrid的CommitEdit事件,然后调用CommitEdit。我不确定为什么需要这最后一步,你可能不必在你的情况下调用CommitEdit。
DataGrid.CommitEditCommand.Execute(this.DataGridWorkItems, this.DataGridWorkItems);
yourDataGrid.CommitEdit(DataGridEditingUnit.Row, false);
2 /在键盘的“返回”键上模拟笔划:
var keyEventArgs = new KeyEventArgs(InputManager.Current.PrimaryKeyboardDevice,PresentationSource.FromDependencyObject(yourDataGrid), System.Environment.ProcessorCount, Key.Return);
keyEventArgs.RoutedEvent = UIElement.KeyDownEvent;
yourDataGrid.RaiseEvent(keyEventArgs);
我选择了最后一个解决方案,因为我在第一个解决方案中有一些可疑的副作用。
不幸的是,其他答案仅在某些情况下解决了问题。例如,如果其中一个单元在切换选项卡时出现验证错误,则其他解决方案将失败。
问题是当IsEnabled被更改时,CanUserAddRows会被更改并触发NewItemPlaceholderPosition被重置。为了解决这个bug,我继承了DataGrid类,并为CanUserAddRowsProperty的CoerceValueCallback添加了一些逻辑。
namespace CustomControls
{
using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Utilities;
public class FixedDataGrid : DataGrid
{
static FixedDataGrid()
{
var originalPropertyChangedCallback = CanUserAddRowsProperty.GetMetadata(typeof(DataGrid)).PropertyChangedCallback;
var originalCoerceValueCallback = CanUserAddRowsProperty.GetMetadata(typeof(DataGrid)).CoerceValueCallback;
CanUserAddRowsProperty.OverrideMetadata(typeof(FixedDataGrid), new FrameworkPropertyMetadata(true,
originalPropertyChangedCallback,
(d, e) =>
{
var ths = ((FixedDataGrid) d);
// Fixes System.InvalidOperationException: 'NewItemPlaceholderPosition' is not allowed during a transaction begun by 'AddNew'.
if (ths.IsEnabled) return originalCoerceValueCallback(d, e);
if (!((IEditableCollectionViewAddNewItem) ths.Items).CanAddNewItem &&
!((IEditableCollectionViewAddNewItem) ths.Items).CanCancelEdit)
return originalCoerceValueCallback(d, e);
ths.CancelEdit();
ReflectionUtils.InvokeMethod(ths, "CancelRowItem");
ReflectionUtils.InvokeMethod(ths, "UpdateNewItemPlaceholder", false);
ReflectionUtils.SetProperty(ths, "HasCellValidationError", false);
CommandManager.InvalidateRequerySuggested();
return originalCoerceValueCallback(d, e);
}));
}
}
}
namespace Utilities
{
using System;
using System.Reflection;
public class ReflectionUtils
{
public static void InvokeMethod(object obj, string name, params object[] args)
{
InvokeMethod(obj, obj.GetType(), name, args);
}
public static void InvokeMethod(object obj, Type type, string name, params object[] args)
{
var method = type.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (method == null)
{
if (type.BaseType == null)
throw new MissingMethodException($"Couldn't find method {name} in {type}");
InvokeMethod(obj, type.BaseType, name, args);
return;
}
method.Invoke(obj, args);
}
public static T InvokeMethod<T>(object obj, string name, params object[] args)
{
return InvokeMethod<T>(obj, obj.GetType(), name, args);
}
public static T InvokeMethod<T>(object obj, Type type, string name, params object[] args)
{
var method = type.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (method == null)
{
if (type.BaseType == null)
throw new MissingMethodException($"Couldn't find method {name} in {type}");
return InvokeMethod<T>(obj, type.BaseType, name, args);
}
return (T) method.Invoke(obj, args);
}
public static T GetProperty<T>(object obj, string name)
{
return GetProperty<T>(obj, obj.GetType(), name);
}
public static T GetProperty<T>(object obj, Type type, string name)
{
var prop = type
.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null)
{
if (type.BaseType == null)
throw new MissingMethodException($"Couldn't find property {name} in {type}");
return GetProperty<T>(obj, type.BaseType, name);
}
return (T) prop
.GetGetMethod(nonPublic: true).Invoke(obj, new object[] { });
}
public static void SetProperty<T>(object obj, string name, T val)
{
SetProperty(obj, obj.GetType(), name, val);
}
public static void SetProperty<T>(object obj, Type type, string name, T value)
{
var prop = type
.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null)
{
if (type.BaseType == null)
throw new MissingMethodException($"Couldn't find property {name} in {type}");
SetProperty(obj, type.BaseType, name, value);
return;
}
prop.GetSetMethod(nonPublic: true).Invoke(obj, new object[] {value});
}
}
}
此代码的工作方式是,当更新IsEnabled时,CanUserAddRows会更改并触发NewItemPlaceholderPosition的setter。通过在设置NewItemPlaceholderPosition之前调用CancelRowItem和UpdateNewItemPlaceholder,我们立即取消事务(不足以调用CancelEdit)。将HasCellValidationError设置为false还有助于从出现验证错误时出现的某些极端情况中恢复。
我使用了holmes的答案,但没有正确地为我工作。所以我改变了一点点。
这是我的解决方案:
首先,由于我使用MVVM,我将此代码添加到datagrid:
<i:Interaction.Triggers>
<i:EventTrigger EventName="InitializingNewItem">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="OnDataGridInitializingNewItem"/>
</i:EventTrigger>
</i:Interaction.Triggers>
命名空间是这些:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
然后,我将此代码添加到ViewModel并设置DataGrid:
private DataGrid _dg { get; set; }
public void OnDataGridInitializingNewItem(object sender, InitializingNewItemEventArgs e)
{
if (_dg == null)
_dg = (DataGrid)sender;
}
毕竟,在需要时,我运行了这段代码:
_dg.CommitEdit();
最后它工作得很好:)
PS:首先,我尝试过CancelEdit方法而不是CommitEdit。它工作,我去了另一个像弹出窗口打开的视图。当我完成要做的事情并返回视图时,最后添加的行已经消失。但它致力于数据库。重新打开视图后,它就在那里。