在'addnew'开始的交易中,不允许WPF数据网格'newitemplaceholderposition'

时间:2011-04-15 15:29:19

标签: wpf datagrid

我有一个tabControl。在其中一个tabitem中我有一个usercontrol,它包含一个具有CanUserAddRows =“True”的数据网格。用户可以在列中键入数据并按下输入创建的新行(我假设datagrid执行此CanzserAddRows =“True”)。 问题是当我输入数据并更改标签时,我得到此异常“在'Addnew'开始的交易期间不允许使用”WPF datagrid'newitemplaceholderposition'“

有任何建议如何避免吗?

我试图将dg.CommitEdit()放在usercontrol.unloaded()上。我没有得到例外,但我没有得到新的一行。

5 个答案:

答案 0 :(得分:3)

我遇到了同样的问题。找到两种可能的解决方法:

1 /触发DataGrid的CommitEdit事件,然后调用CommitEdit。我不确定为什么需要这最后一步,你可能不必在你的情况下调用CommitEdit。

        DataGrid.CommitEditCommand.Execute(this.DataGridWorkItems, this.DataGridWorkItems);

        yourDataGrid.CommitEdit(DataGridEditingUnit.Row, false);

2 /在键盘的“Return”键上模拟一个笔划:

        var keyEventArgs = new KeyEventArgs(InputManager.Current.PrimaryKeyboardDevice,PresentationSource.FromDependencyObject(yourDataGrid), System.Environment.ProcessorCount, Key.Return);
        keyEventArgs.RoutedEvent = UIElement.KeyDownEvent;
        yourDataGrid.RaiseEvent(keyEventArgs);

我选择了最后一个解决方案,因为我对第一个解决方案产生了一些可疑的副作用。

答案 1 :(得分:3)

我遇到了同样的问题......这里有一些片段描述了我是如何解决它的。请注意,在我的情况下,我想拒绝更改以避免错误。如果您想提交更改,这可能会引导您朝着正确的方向前进。

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();

答案 2 :(得分:1)

不幸的是,其他答案只能在某些情况下解决问题。例如,如果一个单元格在切换选项卡时出现验证错误,则其他解决方案将失败。

问题在于,更改IsEnabled时,CanUserAddRows会更改,并且会触发NewItemPlaceholderPosition重置。要解决此错误,我继承了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的设置器。通过在设置NewItemPlaceholderPosition之前调用CancelRowItem和UpdateNewItemPlaceholder,我们可以立即取消交易(不足以调用CancelEdit)。将HasCellValidationError设置为false还可以帮助您从遇到验证错误时出现的一些极端情况中恢复过来。

答案 3 :(得分:0)

我使用过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。它工作,我去了另一个像弹出窗口打开的视图。当我完成要做的事情并返回视图时,最后添加的行已经消失。但它致力于数据库。重新打开视图后,它就在那里。

答案 4 :(得分:0)

我遇到了这样的问题,但就我而言,网格被包裹在AdornerDecorator中,将其删除,一切正常