此视图不允许WPF“EditItem”

时间:2017-09-15 14:07:48

标签: c# wpf datagrid

我知道有很多关于该错误的问题(12345等),但我找不到解释这个错误的原因并适合我的情况。如果我错过了一个,请告诉我!

首先,我使用自定义类(不是ItemsSource或任何其他.NET内置的可观察集合)绑定到我的DataGrid ObservableCollection。在向您展示其代码之前,让我解释一下我是如何想到它的(我的假设可能是错误的)。

在我看来,要成为可绑定的,集合必须至少实现IEnumerableINotifyCollectionChanged。 IEnumerable以便视图获取要显示的项目(感谢GetEnumerator方法)和INotifyCollectionChanged以便视图知道集合上的更改。

所以我最终得到了这个课程:

public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IEnumerable<TValue>, INotifyCollectionChanged
{
    #region fields

    private IDictionary<TKey, TValue> _innerDictionary;

    #endregion

    #region properties

    public int Count { get { return _innerDictionary.Count; } }

    public ICollection<TKey> Keys { get { return _innerDictionary.Keys; } }

    public ICollection<TValue> Values { get { return _innerDictionary.Values; } }

    public bool IsReadOnly { get { return false; } }

    #endregion

    #region indexors

    public TValue this[TKey key]
    {
        get { return _innerDictionary[key]; }
        set { this.InternalAdd(new KeyValuePair<TKey, TValue>(key, value)); }
    }

    #endregion

    #region events

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion

    #region constructors

    public ObservableDictionary()
    {
        _innerDictionary = new Dictionary<TKey, TValue>();
    }

    public ObservableDictionary(int capacity)
    {
        _innerDictionary = new Dictionary<TKey, TValue>(capacity);
    }

    public ObservableDictionary(IEqualityComparer<TKey> comparer)
    {
        _innerDictionary = new Dictionary<TKey, TValue>(comparer);
    }

    public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _innerDictionary = new Dictionary<TKey, TValue>(dictionary);
    }

    public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer)
    {
        _innerDictionary = new Dictionary<TKey, TValue>(capacity, comparer);
    }

    public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
    {
        _innerDictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
    }

    #endregion

    #region public methods

    public bool ContainsKey(TKey key)
    {
        return _innerDictionary.ContainsKey(key);
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _innerDictionary.Contains(item);
    }

    public void Add(TKey key, TValue value)
    {
        this.InternalAdd(new KeyValuePair<TKey, TValue>(key, value));
    }

    public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> items)
    {
        if (!items.Any())
        {
            return;
        }

        var added = new List<TValue>();
        var removed = new List<TValue>();

        foreach (var item in items)
        {
            TValue value;
            if (_innerDictionary.TryGetValue(item.Key, out value))
            {
                removed.Add(value);
            }

            added.Add(item.Value);
            _innerDictionary[item.Key] = item.Value;
        }

        this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, null));

        if (removed.Count > 0)
        {
            this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, removed));
        }
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        this.InternalAdd(item);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _innerDictionary.TryGetValue(key, out value);
    }

    public bool Remove(TKey key)
    {
        return this.InternalRemove(key);
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return this.InternalRemove(item.Key);
    }

    public void Clear()
    {
        _innerDictionary.Clear();
        this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _innerDictionary.CopyTo(array, arrayIndex);
    }

    public IEnumerator<TValue> GetEnumerator()
    {
        return Values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
    {
        return _innerDictionary.GetEnumerator();
    }

    #endregion

    #region private methods

    /// <summary>
    /// Adds the specified value to the internal dictionary and indicates whether the element has actually been added. Fires the CollectionChanged event accordingly.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    private void InternalAdd(KeyValuePair<TKey, TValue> item)
    {
        IList added = new TValue[] { item.Value };

        TValue value;
        if (_innerDictionary.TryGetValue(item.Key, out value))
        {
            this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, new TValue[] { value }));
        }

        _innerDictionary[item.Key] = item.Value;
        this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, null));
    }

    /// <summary>
    /// Remove the specified key from the internal dictionary and indicates whether the element has actually been removed. Fires the CollectionChanged event accordingly.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    private bool InternalRemove(TKey key)
    {
        TValue value;
        if (_innerDictionary.TryGetValue(key, out value))
        {
            _innerDictionary.Remove(key);
            this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, null, new TValue[] { value }));
        }

        return value != null;
    }

    #endregion
}

它隐式实现了IEnumerable<TValue>.GetEnumerator并明确地实现了其他GetEnumerator方法(IDictionaryIEnumerable),以便我的视图只显示我的字典的值,而我在调用CollectionChanged事件周围映射添加/删除方法。

我的ViewModel定义如下:

class MyViewModel
{
    public ObservableDictionary<string, Foo> Foos { get; private set; }

    public MyViewModel()
    {
        this.Foos = new ObservableDictionary<string, Foo>();
    }
}

并将其绑定到我的观点:

<DataGrid ItemsSource="{Binding Facts}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" Width="*" />
        <DataGridTextColumn Header="Type" Binding="{Binding Type}" IsReadOnly="True" Width="*" />
        <DataGridTextColumn Header="Value" Binding="{Binding Value}" IsReadOnly="False" Width="*" />
    </DataGrid.Columns>
</DataGrid>

然后,当我尝试编辑Value时,我收到指定的错误:

  

此视图不允许使用'EditItem'

当我在代码中添加一些断点时,我从未到达ObservableDictionary索引器设置器或Foo.Value setter。

我的想法是关于视图如何从绑定的集合中获取项目更正?为什么我收到此错误和/或如何向EditItem授权我的观看次数?

1 个答案:

答案 0 :(得分:2)

如果您希望能够编辑ObservableDictionary<TKey, TValue>中的数据,那么您的源集合类型(IList)应该实现DataGrid界面。

每当您绑定到WPF中的某个集合属性时,您始终绑定到自动生成的视图,而不是绑定到实际的源集合本身。

运行时为您创建的视图类型取决于源集合的类型,并且源集合必须为{{1}的内部编辑功能实现非通用IList接口控制工作。