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

问题描述 投票:6回答:4

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

有什么建议可以避免吗?

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

wpf datagrid
4个回答
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();

3
投票

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

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

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


1
投票

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

问题是当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还有助于从出现验证错误时出现的某些极端情况中恢复。


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

© www.soinside.com 2019 - 2024. All rights reserved.