.NET 4.0 Indexer with ObservableCollection

丶灬走出姿态 提交于 2019-12-21 06:42:15

问题


What is the .NET C# syntax for an ObservableCollection with an indexer? I would like an ObservableColletion and refer to the items by ordinal position or a string name. I know you the use this to denote an indexer but I don't know how to put that in an ObservableCollection. Thanks

Thanks for the 4 answers. I know how create and ObservableCollection and I know how to create an indexer. I don't know how to combine them. I am asking for sample code for an ObservableCollection with an ordinal and string index. Thank again


回答1:


ObservableCollection inherits from Collection, so it already has position-based indexing.

For string-based indexing, you can look into peoples implementations of ObservableDictionary.

Personally, for better performance, I've created a HashedObservableCollection deriving from ObservableCollection which contains a Dictionary of keys to indexes to speed lookup time. By overriding InsertItem, RemoveItem, and ClearItems, you keep the dictionary in sync.

In my example, the keys can be of any type but we assume the key never changes - if an item is replaced, it is replaced with an object with the same key. If you want to simplify this, you can replace TKey with String.

Code:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace foson.Utils
{
    /// <summary>
    /// Represents BindableCollection indexed by a dictionary to improve lookup/replace performance.
    /// </summary>
    /// <remarks>
    /// Assumes that the key will not change and is unique for each element in the collection.
    /// Collection is not thread-safe, so calls should be made single-threaded.
    /// </remarks>
    /// <typeparam name="TValue">The type of elements contained in the BindableCollection</typeparam>
    /// <typeparam name="TKey">The type of the indexing key</typeparam>
    public class HashedBindableCollection<TValue, TKey> : ObservableCollection<TValue>
    {

        protected internal Dictionary<TKey, int> indecies = new Dictionary<TKey, int>();
        protected internal Func<TValue, TKey> _keySelector;

        /// <summary>
        /// Create new HashedBindableCollection
        /// </summary>
        /// <param name="keySelector">Selector function to create key from value</param>
        public HashedBindableCollection(Func<TValue, TKey> keySelector)
            : base()
        {
            if (keySelector == null) throw new ArgumentException("keySelector");
            _keySelector = keySelector;
        }

        #region Protected Methods
        protected override void InsertItem(int index, TValue item)
        {
            var key = _keySelector(item);
            if (indecies.ContainsKey(key))
                throw new DuplicateKeyException(key.ToString());

            if (index != this.Count)
            {
                foreach (var k in indecies.Keys.Where(k => indecies[k] >= index).ToList())
                {
                    indecies[k]++;
                }
            }

            base.InsertItem(index, item);
            indecies[key] = index;

        }

        protected override void ClearItems()
        {
            base.ClearItems();
            indecies.Clear();
        }


        protected override void RemoveItem(int index)
        {
            var item = this[index];
            var key = _keySelector(item);

            base.RemoveItem(index);

            indecies.Remove(key);

            foreach (var k in indecies.Keys.Where(k => indecies[k] > index).ToList())
            {
                indecies[k]--;
            }
        }
        #endregion

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

        /// <summary>
        /// Gets or sets the element with the specified key.  If setting a new value, new value must have same key.
        /// </summary>
        /// <param name="key">Key of element to replace</param>
        /// <returns></returns>
        public virtual TValue this[TKey key]
        {

            get { return this[indecies[key]]; }
            set
            {
                //confirm key matches
                if (!_keySelector(value).Equals(key))
                    throw new InvalidOperationException("Key of new value does not match");

                if (!indecies.ContainsKey(key))
                {
                    this.Add(value);
                }
                else
                {
                    this[indecies[key]] = value;
                }
            }
        }

        /// <summary>
        /// Replaces element at given key with new value.  New value must have same key.
        /// </summary>
        /// <param name="key">Key of element to replace</param>
        /// <param name="value">New value</param>
        /// 
        /// <exception cref="InvalidOperationException"></exception>
        /// <returns>False if key not found</returns>
        public virtual bool Replace(TKey key, TValue value)
        {
            if (!indecies.ContainsKey(key)) return false;
            //confirm key matches
            if (!_keySelector(value).Equals(key))
                throw new InvalidOperationException("Key of new value does not match");

            this[indecies[key]] = value;
            return true;

        }

        public virtual bool Remove(TKey key)
        {
            if (!indecies.ContainsKey(key)) return false;

            this.RemoveAt(indecies[key]);
            return true;

        }

    }
    public class DuplicateKeyException : Exception
    {

        public string Key { get; private set; }
        public DuplicateKeyException(string key)
            : base("Attempted to insert duplicate key " + key + " in collection")
        {
            Key = key;
        }
    }
}



回答2:


here is my idea, hope this helps to find your solution

using System.Collections.ObjectModel;

namespace WPFValidation
{
  public class CustomObservableCollection<T> : ObservableCollection<T>
  {
    public T this[string key] {
      get {
        // you must implement some code to do this one
        T item = GetItemWithAKey(key);
        return item;
      }
      set {
        T item = GetItemWithAKey(key);
        if (item != null) {
          // set the given value toi the item
          this.SetItemValue(item, value);
        }
      }
    }

    private T GetItemWithAKey(string key) {
      // find the item with teh given key
      return default(T);
    }
  }

  public class TestClass
  {
    public TestClass() {
      var coll = new CustomObservableCollection<CustomKeyedClass>();
      coll.Add(new CustomKeyedClass("One"));
      coll.Add(new CustomKeyedClass("Two"));
      var item = coll["One"];
      var item2 = coll[1];
    }
  }
}



回答3:


If i understand your question correctly, you can use this example

http://msdn.microsoft.com/en-us/library/ms132434.aspx




回答4:


I think this is the syntax that you are looking for:

  //  create a generic ObservableCollection  - I used object, but you can use any Type
  var collection = new ObservableCollection<object>();

  //  set the item at the index.  
  collection[0] = new object();

Documentation for ObservableCollection<T>: http://msdn.microsoft.com/en-us/library/ms668604.aspx

Documentation for the Indexer (aka the 'Item' property): http://msdn.microsoft.com/en-us/library/ms132434.aspx

Based on your comments it sounds like you are looking for an ObservableDictionary as opposed to an ObservableCollection. .NET does not have such a collection built in but a quick google search found these two implementations:

  • http://blogs.microsoft.co.il/blogs/shimmy/archive/2010/12/26/observabledictionary-lt-tkey-tvalue-gt-c.aspx

  • http://uberpwn.wordpress.com/2011/03/12/my-c-observabledictionary/



来源:https://stackoverflow.com/questions/8157140/net-4-0-indexer-with-observablecollection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!