的ObservableCollection元素智能变换/投影包装 [英] ObservableCollection element-wise Transform/Projection Wrapper

查看:134
本文介绍了的ObservableCollection元素智能变换/投影包装的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在WPF中创建的ViewModels有时需要转换数据是在<一个可用href="http://msdn.microsoft.com/en-us/library/vstudio/ms668604%28v=vs.90%29.aspx"><$c$c>ObservableCollection (源集合)放入包装元素扩展/限制/项目的原始元素(目标集合)的集合,而元件的数量和顺序总是反映原始集合

When creating ViewModels in WPF it's sometimes necessary to transform data that is available in an ObservableCollection (the source collection) into a collection of wrapper elements that extend/restrict/project the original elements (the target collection), while the number and order of the elements always mirror the original collection.

就像选择的扩展方法,不同之处在于它被不断更新,因此可以是使用WPF绑定。

Just like the Select extension method, except that it is continuously updated and can therefore be used for WPF bindings.

如果一个元件被添加到源索引的x,同样的元件的包装是在相同的索引中的x目标集合添加。如果在索引y中的元素源集合被删除,在指数预测的元素在目标集合将被删除。

If an element is added to the source at index x, the Wrapper of the same element is added at the same index x in the target collection. If the element at index y is removed in the source collection, the element at index y is removed in the target collection.

说有一个<一个href="http://msdn.microsoft.com/en-us/library/vstudio/ms668604%28v=vs.90%29.aspx"><$c$c>ObservableCollection<ClassA>,但我需要绑定是一个<一个href="http://msdn.microsoft.com/en-us/library/ms668620.aspx"><$c$c>ReadOnlyObservableCollection<ClassB> (或同等学历),其中 ClassB的 - > ClassA的如下:

Say there is an ObservableCollection<ClassA>, but what I need to bind to is an ReadOnlyObservableCollection<ClassB> (or equivalent), where ClassB -> ClassA as follows:

class ClassB : INotifyPropertyChanged, IDisposable
{
    public ClassB(ClassA a)
    {
        Wrapped = a;
        (Wrapped as INotifyPropertyChanged).PropertyChanged+=WrappedChanged;
    }
    public ClassA Wrapped { get; private set; }
    public int SomeOtherProperty { get { return SomeFunction(Wrapped); }
    WrappedChanged(object s, NotifyPropertyChangedArgs a) { ... }
    ...
}

我可以写我自己的 TemplatedTransformCollectionWrapper ,在那里我可以这样写:

I can write my own TemplatedTransformCollectionWrapper, where I can write this:

ObservableCollection<ClassA> source;
TemplatedTransformCollectionWrapper theCollectionThatWillBeUsedInABinding
    = TemplatedTransformCollectionWrapper(source, classA => new ClassB(classA));

TemplatedTransformCollectionWrapper理想的包装实现 INotifyCollectionChanged 所有集合,并正确处理所有可能的添加,删除,替换原来,包裹,收集操作。

TemplatedTransformCollectionWrapper ideally wraps all collections that implement INotifyCollectionChanged and correctly handles all possible add, remove, replace operations of the original, wrapped, collection.

这是不平凡的正确写入 TemplatedTransformCollectionWrapper ,它似乎是这样的事情,别人已经做过,也许它的核心框架,甚至一部分。但我不能找到它。

It's not trivial to write TemplatedTransformCollectionWrapper correctly and it seems to be the kind of thing that someone else has already done, maybe it's even part of the core framework. But I can't find it.

推荐答案

我张贴我的解决方法 - 这是一个自定义类 - 在这里。仍然希望更好的答案。

I'm posting my workaround - which is a custom class - here. Still hoping for better answers.

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

namespace ViewLayer
{
    public class TransformObservableCollection<T,Source> : INotifyCollectionChanged, IList, IReadOnlyList<T>, IDisposable
    {
        public TransformObservableCollection(ObservableCollection<Source> wrappedCollection, Func<Source,T> transform)
        {
            m_WrappedCollection = wrappedCollection;
            m_TransformFunc = transform;
            ((INotifyCollectionChanged)m_WrappedCollection).CollectionChanged += TransformObservableCollection_CollectionChanged;
            m_TransformedCollection = new ObservableCollection<T>(m_WrappedCollection.Select(m_TransformFunc));
        }
        public void Dispose()
        {
            if (m_WrappedCollection == null) return;
            ((INotifyCollectionChanged)m_WrappedCollection).CollectionChanged -= TransformObservableCollection_CollectionChanged;
            m_WrappedCollection = null;
        }
        void TransformObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewItems == null || e.NewItems.Count != 1)
                        break;
                    m_TransformedCollection.Insert(e.NewStartingIndex,m_TransformFunc((Source)e.NewItems[0]));
                    return;
                case NotifyCollectionChangedAction.Move:
                    if (e.NewItems == null || e.NewItems.Count != 1 || e.OldItems == null || e.OldItems.Count != 1)
                        break;
                    m_TransformedCollection.Move(e.OldStartingIndex, e.NewStartingIndex);
                    return;
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldItems == null || e.OldItems.Count != 1)
                        break;
                    m_TransformedCollection.RemoveAt(e.OldStartingIndex);
                    return;
                case NotifyCollectionChangedAction.Replace:
                    if (e.NewItems == null || e.NewItems.Count != 1 || e.OldItems == null || e.OldItems.Count != 1 || e.OldStartingIndex != e.NewStartingIndex)
                        break;
                    m_TransformedCollection[e.OldStartingIndex] = m_TransformFunc((Source)e.NewItems[0]);
                    return;
            } // This  is most likely called on a Clear(), we don't optimize the other cases (yet)
            m_TransformedCollection.Clear();
            foreach (var item in m_WrappedCollection)
                m_TransformedCollection.Add(m_TransformFunc(item));
        }

        #region IList Edit functions that are unsupported because this collection is read only
        public int Add(object value) { throw new InvalidOperationException(); }
        public void Clear() { throw new InvalidOperationException(); }
        public void Insert(int index, object value) { throw new InvalidOperationException(); }
        public void Remove(object value) { throw new InvalidOperationException(); }
        public void RemoveAt(int index) { throw new InvalidOperationException(); }
        #endregion IList Edit functions that are unsupported because this collection is read only

        #region Accessors
        public T this[int index] { get { return m_TransformedCollection[index]; } }
        object IList.this[int index] { get { return m_TransformedCollection[index]; } set { throw new InvalidOperationException(); } }
        public bool Contains(T value) { return m_TransformedCollection.Contains(value); }
        bool IList.Contains(object value) { return ((IList)m_TransformedCollection).Contains(value); }
        public int IndexOf(T value) { return m_TransformedCollection.IndexOf(value); }
        int IList.IndexOf(object value) { return ((IList)m_TransformedCollection).IndexOf(value); }
        public int Count { get { return m_TransformedCollection.Count; } }
        public IEnumerator<T> GetEnumerator() { return m_TransformedCollection.GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)m_TransformedCollection).GetEnumerator(); }
        #endregion Accessors

        public bool IsFixedSize { get { return false; } }
        public bool IsReadOnly { get { return true; } }
        public void CopyTo(Array array, int index) { ((IList)m_TransformedCollection).CopyTo(array, index); }
        public void CopyTo(T[] array, int index) { m_TransformedCollection.CopyTo(array, index); }
        public bool IsSynchronized { get { return false; } }
        public object SyncRoot { get { return m_TransformedCollection; } }

        ObservableCollection<T> m_TransformedCollection;
        ObservableCollection<Source> m_WrappedCollection;
        Func<Source, T> m_TransformFunc;

        event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
        {
            add { ((INotifyCollectionChanged)m_TransformedCollection).CollectionChanged += value; }
            remove { ((INotifyCollectionChanged)m_TransformedCollection).CollectionChanged -= value; }
        }
    }
}

这篇关于的ObservableCollection元素智能变换/投影包装的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆