2017-09-15 96 views
2

我知道有很多關於這個錯誤的問題(12345等),但我不能找到一個解釋的原因這個錯誤適合我的情況。讓我知道如果我錯過了!WPF「EditItem」不允許這種觀點

首先,我使用自定義類(不是ObservableCollection或任何其他.NET內置的可觀察集合)綁定到我的DataGrid ItemsSource。在向你展示它的代碼之前,讓我解釋我是怎麼想的(我的假設可能是錯的)。

在我看來,爲了可以綁定,集合必須實現至少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事件的添加/刪除方法。

我的視圖模型的定義是這樣的:

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> 

然後,當我嘗試編輯的價值,我得到指定的錯誤:

'EditItem' is not allowed for this view

當我在我的代碼中放置了一些斷點時,我從來沒有達到ObservableDictionary indexor setter no r Foo.Value二傳手。

我的想法是關於視圖如何從綁定集合中獲取項目的問題?爲什麼我得到這個錯誤和/或我如何授權我的觀點爲EditItem?如果你希望能夠在一個DataGrid編輯數據

+0

[只是一個說明](https://stackoverflow.com/a/4225429/3283203)關於「我永遠不會到達ObservableDictionary indexor setter」 – Kilazur

回答

2

你的源集合類型(ObservableDictionary<TKey, TValue>)應該實現IList接口。

每當您綁定到WPF中的某個集合屬性時,您總是綁定到自動生成的視圖,而不是綁定到實際的源集合本身。

運行時爲您創建的視圖類型取決於源集合的類型,並且源集合必須實現非通用的IList接口,才能使DataGrid控件的內部編輯功能正常工作。

+0

只是'IList'或'IList '?爲什麼'IEnumerable '還不夠?我也試圖找到根本原因,而不僅僅是修復,所以你能解釋爲什麼這會起作用嗎? – fharreau

+1

非通用IList接口:https://msdn.microsoft。COM/EN-US /庫/ system.collections.ilist(V = vs.110)的.aspx。看我的編輯。 – mm8

+0

感謝有關如何綁定工作的精度。所以在我的情況下實現IEnumerable 是沒用的? – fharreau