wpf ObservableDictionary for C#

zynd9foi  于 2023-08-07  发布在  C#
关注(0)|答案(7)|浏览(131)

我尝试使用以下ObservableDictionary的实现:ObservableDictionary (C#)的数据。
当我在将字典绑定到DataGrid时使用以下代码时:

ObserveableDictionary<string,string> dd=new ObserveableDictionary<string,string>();
....
dd["aa"]="bb";
....
dd["aa"]="cc";

字符串
dd["aa"]="cc";,我得到以下异常

Index was out of range. Must be non-negative and less than the size of the 
collection. Parameter name: index


CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem)中,以下方法会引发此异常:

private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem)
{
  OnPropertyChanged();

  if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem));
}


index参数似乎对应于KeyValuePair<TKey, TValue> oldItem
KeyValuePair<TKey, TValue>怎么会超出范围,我应该怎么做才能使它正常工作?

mv1qrgav

mv1qrgav1#

我最后是这么做的

[Serializable]
public class ObservableKeyValuePair<TKey,TValue>:INotifyPropertyChanged
{
    #region properties
    private TKey key;
    private TValue value;

    public TKey Key
    {
        get { return key; }
        set
        {
            key = value;
            OnPropertyChanged("Key");
        }
    }

    public TValue Value
    {
        get { return value; }
        set
        {
            this.value = value;
            OnPropertyChanged("Value");
        }
    } 
    #endregion

    #region INotifyPropertyChanged Members

    [field:NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(name));
    }

    #endregion
}

[Serializable]
public class ObservableDictionary<TKey,TValue>:ObservableCollection<ObservableKeyValuePair<TKey,TValue>>, IDictionary<TKey,TValue>
{

    #region IDictionary<TKey,TValue> Members

    public void Add(TKey key, TValue value)
    {
        if (ContainsKey(key))
        {
            throw new ArgumentException("The dictionary already contains the key");
        }
        base.Add(new ObservableKeyValuePair<TKey, TValue>() {Key = key, Value = value});
    }

    public bool ContainsKey(TKey key)
    {
        //var m=base.FirstOrDefault((i) => i.Key == key);
        var r = ThisAsCollection().FirstOrDefault((i) => Equals(key, i.Key));

        return !Equals(default(ObservableKeyValuePair<TKey, TValue>), r);
    }

    bool Equals<TKey>(TKey a, TKey b)
    {
        return EqualityComparer<TKey>.Default.Equals(a, b);
    }

    private ObservableCollection<ObservableKeyValuePair<TKey, TValue>> ThisAsCollection()
    {
        return this;
    }

    public ICollection<TKey> Keys
    {
        get { return (from i in ThisAsCollection() select i.Key).ToList(); }
    }

    public bool Remove(TKey key)
    {
        var remove = ThisAsCollection().Where(pair => Equals(key, pair.Key)).ToList();
        foreach (var pair in remove)
        {
            ThisAsCollection().Remove(pair);
        }
        return remove.Count > 0;
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        value = default(TValue);
        var r = GetKvpByTheKey(key);
        if (!Equals(r, default(ObservableKeyValuePair<TKey, TValue>)))
        {
            return false;
        }
        value = r.Value;
        return true;
    }

    private ObservableKeyValuePair<TKey, TValue> GetKvpByTheKey(TKey key)
    {
        return ThisAsCollection().FirstOrDefault((i) => i.Key.Equals(key));
    }

    public ICollection<TValue> Values
    {
        get { return (from i in ThisAsCollection() select i.Value).ToList(); }
    }

    public TValue this[TKey key]
    {
        get
        {
            TValue result;
            if (!TryGetValue(key,out result))
            {
                throw new ArgumentException("Key not found");
            }
            return result;
        }
        set
        {
            if (ContainsKey(key))
            {
                GetKvpByTheKey(key).Value = value;
            }
            else
            {
                Add(key, value);
            }
        }
    }

    #endregion

    #region ICollection<KeyValuePair<TKey,TValue>> Members

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

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        var r = GetKvpByTheKey(item.Key);
        if (Equals(r, default(ObservableKeyValuePair<TKey, TValue>)))
        {
            return false;
        }
        return Equals(r.Value, item.Value);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        var r = GetKvpByTheKey(item.Key);
        if (Equals(r, default(ObservableKeyValuePair<TKey, TValue>)))
        {
            return false;
        }
        if (!Equals(r.Value,item.Value))
        {
            return false ;
        }
        return ThisAsCollection().Remove(r);
    }

    #endregion

    #region IEnumerable<KeyValuePair<TKey,TValue>> Members

    public new IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return (from i in ThisAsCollection() select new KeyValuePair<TKey, TValue>(i.Key, i.Value)).ToList().GetEnumerator();
    }

    #endregion
}

字符串
这个实现对于用户来说看起来和感觉上都像字典,而对于WPF来说就像ObservableCollection

yh2wf1be

yh2wf1be2#

类似的数据结构,绑定到Dictionary类型集合
http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/
它提供了一个新的数据结构ObservableDictionary,并在底层Dictionary发生任何更改时触发PropertyChanged

ibrsph3r

ibrsph3r3#

我最终编写了一个类来保存键值对,并使用了该类的集合。我使用的是Caliburn Micro,这是BindableCollection的来源,但ObservableCollection应该以同样的方式工作。我使用MVVM模式。
视图模型

using Caliburn.Micro;

private BindableCollection<KeyValuePair> _items;

public BindableCollection<KeyValuePair> Items
{
  get { return _items; }

  set
  {
    if (_items != value)
    {
      _items = value;
      NotifyOfPropertyChange(() => Items);
    }
  }
}

字符串
自定义keyValuePair

public class KeyValuePair 
{
  public string Key { get; set; }

  public string Value { get; set; }
}


在视图中

<ItemsControl ItemsSource="{Binding Items}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*" />
          <ColumnDefinition Width="2*" />
          <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <TextBox Grid.Column="0"
                 Text="{Binding Key, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Grid.Column="1"
                 Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
      </Grid>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>


我不能只绑定到一个字典,这让我很烦恼,但我发现这比从头开始编写ObservableDictionary并担心更改通知要容易得多。

ut6juiuv

ut6juiuv4#

ObservableDictionary在.Net Framework 4.5版本中添加:-
https://zamjad.wordpress.com/2012/10/12/observabledictionary-in-net-4-5/
下面是最新源代码的链接:
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/MS/Internal/Annotations/ObservableDictionary.cs

flmtquvp

flmtquvp5#

我首先创建了一个名为“ConcurrentObservableCollection”的类,在其中扩展了ObservableCollection函数。

public class ConcurrentObservableCollection<T> : ObservableCollection<T>
{
    private readonly object _lock = new object();

    public new void Add(T value)
    {
        lock (_lock)
        {
            base.Add(value);
        }
    }

    public List<T> ToList()
    {
        lock (_lock)
        {
            var copyList = new List<T>();
            copyList.AddRange(base.Items);
            return copyList;
        }
    }

    public new IEnumerator<T> GetEnumerator()
    {
        lock (_lock)
        {
            return base.GetEnumerator();
        }
    }

    public new bool Remove(T item)
    {
        lock (_lock)
        {
            return base.Remove(item);
        }
    }

    public new void Move(int oldIndex, int newIndex)
    {
        lock (_lock)
        {
            base.Move(oldIndex, newIndex);
        }
    }

    public new bool Contains(T item)
    {
        lock (_lock)
        {
            return base.Contains(item);
        }
    }

    public new void Insert(int index, T item)
    {
        lock (_lock)
        {
            base.Insert(index, item);
        }
    }

    public new int Count()
    {
        lock (_lock)
        {
            return base.Count;
        }
    }

    public new void Clear()
    {
        lock (_lock)
        {
            base.Clear();
        }
    }

    public new T this[int index]
    {
        get
        {
            lock (_lock)
            {
                return base[index];
            }
        }
    }
}

字符串
然后我用新的“ConcurrentObservableCollection”替换了现有的“ObservabeCollection”

4bbkushb

4bbkushb6#

安装Microsoft ParallelExtensionsExtras
现在通过Nuget提供:nuget.org/packages/MSFT.ParallelExtensionsExtras那个库实现了ObservableConcurrentDictionary,我试过了,它工作正常=]
请注意,其中一些功能现在是较新的.NET框架的一部分。ParallelExtensions“Extras”还有价值吗?
Microsoft Tour博客:https://blogs.msdn.microsoft.com/pfxteam/2010/04/04/a-tour-of-parallelextensionsextras/
仅供参考,从.NET ObservableDictionaryGeneral Observable Dictionary Class for DataBinding/WPF C#

bkhjykvo

bkhjykvo7#

即使我使用github的ObservableDictionary,我也遇到了这个异常。我已经在类级别声明了字典变量,后来我试图在访问它的方法中创建一个新示例。
给出异常的OldCode:

public class CName
{
  ObservableDictionary<string, string> _classVariableDictionary = new ObservableDictionary<string, string>();
}

字符串
NewCode工作:

public void MethodName()
{
    ObservableDictionary<string, string> _localVariableDictionary = new ObservableDictionary<string, string>();
}

相关问题