5

私の WPF アプリには、アイテムの値が以前に表示されたアイテムに依存する ItemsControl があります。

ViewModel は可変長の部分に分割されたオーディオ ファイルであり、右側に DateTime が表示されるように表示する必要があり、それを計算する必要があります (各部分の長さしかわかりません。計算する必要があります。実際の開始時間と終了時間、および ItemsControl 上の位置)。

--
  ----
      ------------
                  --
                    --------------------

私の最初のアプローチは を使用することでしたObservableCollection<MyviewModel>が、すぐにいくつかの恐怖が発生しました:

IMultiValueConverter実行時に前の要素しか知らなかったので、返す値を計算し、DataContext のプロパティをその値に設定する5 ウェイ マルチバインディング。

前の要素は、 のバインディングを使用して送信されましたRelativesource.PreviousData

今私の問題は、コンバーターから値を設定した後(明らかに悪いことです)、実際にそれを機能させた後、通常のコレクションにはその要素に順序の概念がないため、さらに先の場合残りの途中にオーディオパートを追加したいのですが、表示がめちゃくちゃです。

さらに、さらにビジネスロジックを実装する場合、このコンバーターで計算されたオーディオ パーツの開始と終了にアクセスする必要があるかもしれませんが、まだ表示されていない場合はどうなりますか?

したがって、そのアプローチはいくつかのレベルで間違っていました。

そこでググって調べて知ったLinkedList。今、私は基本的に Observable LinkedList であるクラスを作成しようとしています (ジェネリックである必要はありません):

public class ObservableSegmentLinkedList : LinkedList<MyViewModel>, INotifyCollectionChanged
    {
        //Overrides ???

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public void OnNotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, e);
            }
        }

        #endregion
    }

そして、問題の核心は、コレクションを変更するメソッド (Addfirst、AddLast など) をオーバーライドできないため、OnNotifyCollectionChanged を適切に呼び出せないことです...

したがって、これらのメソッドごとにオーバーロードを作成できると考えていますが、それはかなり厄介に聞こえます...

要するに、独自のプロパティの 1 つを計算するために、各項目が前の項目の詳細を知っているある種のコレクションが必要です。

手がかりはありますか?これも良い解決策ですか?

ありがとう!

付録、ViewModel は次のようになります。

public class MyViewModel : INotifyPropertyChanged
    {
        private DateTime m_SegmentLength;
        public DateTime SegmentLength
        {
            get { return m_SegmentLength; }
            set
            {
                m_SegmentLength = value;
                NotifyPropertyChanged("SegmentLength");
            }
        }

        private DateTime m_SegmentAdvert;
        public DateTime SegmentAdvert
        {
            get { return m_SegmentAdvert; }
            set
            {
                m_SegmentAdvert = value;
                NotifyPropertyChanged("SegmentAdvert");
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String prop)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        #endregion
    }

編集: Thomas と Will の回答を組み合わせようと思います: コンポジションを使用し (つまり、カスタム オブジェクトから継承するのではなく、LinkedList のインスタンスを保持します)、使用することを意図したメソッドを再定義します (AddAfter、AddFirstなど) 実際の LinkedList メソッドを呼び出した後、OnNotifyPropertychanged を呼び出すだけです。ちょっとした作業ですが、私の問題に対するエレガントな解決策はないと思います...

4

6 に答える 6

6

さて、私はカスタム ジェネリック クラスを作成しました。これは をサポートしIEnumerable、あたかもそれが であるかのように使用されLinkedList<T>ます。唯一の違いは、WPF が変更を通知されることです。

このソリューションは、かなり小さなコレクションでのみ機能することに注意してください。最大で約 30 の要素しか管理する必要がないため、問題はありませんが、このコレクションを変更するたびに、「リセット」と見なされます。

ここに解決策があります:

    /// <summary>
    /// This class is a LinkedList that can be used in a WPF MVVM scenario. Composition was used instead of inheritance,
    /// because inheriting from LinkedList does not allow overriding its methods.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObservableLinkedList<T> : INotifyCollectionChanged, IEnumerable
    {
        private LinkedList<T> m_UnderLyingLinkedList;

        #region Variables accessors
        public int Count
        {
            get { return m_UnderLyingLinkedList.Count; }
        }

        public LinkedListNode<T> First
        {
            get { return m_UnderLyingLinkedList.First; }
        }

        public LinkedListNode<T> Last
        {
            get { return m_UnderLyingLinkedList.Last; }
        }
        #endregion

        #region Constructors
        public ObservableLinkedList()
        {
            m_UnderLyingLinkedList = new LinkedList<T>();
        }

        public ObservableLinkedList(IEnumerable<T> collection)
        {
            m_UnderLyingLinkedList = new LinkedList<T>(collection);
        }
        #endregion

        #region LinkedList<T> Composition
        public LinkedListNode<T> AddAfter(LinkedListNode<T> prevNode, T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddAfter(prevNode, value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
        {
            m_UnderLyingLinkedList.AddAfter(node, newNode);
            OnNotifyCollectionChanged();
        }

        public LinkedListNode<T> AddBefore(LinkedListNode<T> node, T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddBefore(node, value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
        {
            m_UnderLyingLinkedList.AddBefore(node, newNode);
            OnNotifyCollectionChanged();
        }

        public LinkedListNode<T> AddFirst(T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddFirst(value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddFirst(LinkedListNode<T> node)
        {
            m_UnderLyingLinkedList.AddFirst(node);
            OnNotifyCollectionChanged();
        }

        public LinkedListNode<T> AddLast(T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddLast(value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddLast(LinkedListNode<T> node)
        {
            m_UnderLyingLinkedList.AddLast(node);
            OnNotifyCollectionChanged();
        }

        public void Clear()
        {
            m_UnderLyingLinkedList.Clear();
            OnNotifyCollectionChanged();
        }

        public bool Contains(T value)
        {
            return m_UnderLyingLinkedList.Contains(value);
        }

        public void CopyTo(T[] array, int index)
        {
            m_UnderLyingLinkedList.CopyTo(array, index);
        }

        public bool LinkedListEquals(object obj)
        {
            return m_UnderLyingLinkedList.Equals(obj);
        }

        public LinkedListNode<T> Find(T value)
        {
            return m_UnderLyingLinkedList.Find(value);
        }

        public LinkedListNode<T> FindLast(T value)
        {
            return m_UnderLyingLinkedList.FindLast(value);
        }

        public Type GetLinkedListType()
        {
            return m_UnderLyingLinkedList.GetType();
        }

        public bool Remove(T value)
        {
            bool ret = m_UnderLyingLinkedList.Remove(value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void Remove(LinkedListNode<T> node)
        {
            m_UnderLyingLinkedList.Remove(node);
            OnNotifyCollectionChanged();
        }

        public void RemoveFirst()
        {
            m_UnderLyingLinkedList.RemoveFirst();
            OnNotifyCollectionChanged();
        }

        public void RemoveLast()
        {
            m_UnderLyingLinkedList.RemoveLast();
            OnNotifyCollectionChanged();
        }
        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public void OnNotifyCollectionChanged()
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (m_UnderLyingLinkedList as IEnumerable).GetEnumerator();
        }

        #endregion
    }

コメントで @AndrewS が述べたように、LinkedListNode は、List プロパティから ObservableLinkedList を返すカスタム クラスに置き換える必要があります。

于 2011-08-09T15:40:10.183 に答える
3

LinkedList<T>継承用に設計されていません。そのメソッドのほとんどは仮想ではないため、オーバーライドするものはありません。その実装と実装を再利用したい場合はINotifyCollectionChanged、継承ではなく構成を使用してください。

しかし、いずれにせよ、リンク リストはインデックスによるランダム アクセスをサポートしていないため、監視可能なリンク リストを実装する意味はありませCollectionChangedNotifyCollectionChangedAction.Reset。非常に効率的です)

于 2011-08-09T12:58:46.130 に答える
1

これは良い解決策です。LinkedList の独自の実装を作成するだけで済みます。

LinkedList<T>は、リンクの多い listy インターフェイスを実装していないため、メソッド/プロパティに関しては自分で行う必要があります。のパブリック メソッドとプロパティを複製することをお勧めしますLinkedList<T>。これにより、コレクションの実際のインスタンスをバッキング ストアとして使用できます。

于 2011-08-09T12:59:33.450 に答える
0

2 つの異なる問題があるように思えます。1 つは表示するアイテムのリストを管理することで、もう 1 つはアイテムが前後のアイテムにアクセスできるようにすることです。

それが私がそれにアプローチする方法です: item クラスにプロパティを追加し、最初にコレクションにデータを入力するときにそれらを設定し、リストからアイテムを挿入および削除するときにそれらを更新します PreviousNext

本当に夢中になって汎用ソリューションを作成したい場合は、ILinkedListNodeインターフェイスを実装してからサブクラス化し、さまざまな挿入メソッドと削除メソッドをオーバーライドして、アイテムとプロパティObservableCollection<T> where T : ILinkedListNodeを更新できます。ソリューションを再利用可能にする必要がある場合は、そうします。PreviousNext

そうでない場合は、コレクションをラップするビュー モデル クラスを作成し、それをItemsプロパティとして公開してから、UI がバインドできる挿入および削除コマンドを実装します。

于 2011-08-09T13:25:20.877 に答える
0

クラスを次のように作成した場合:

public class ObservableSegmentLinkedList<T> : LinkedList<T>, INotifyCollectionChanged 
{
...
public new void AddFirst(T value)
{
.. do something to make it your own - you can still call the base method so in effect you override it with the new keyword.
}
}

new キーワードでメソッドをオーバーライドしても問題はありません。
クラス自体に、リンク リスト タイプと一致する TYPE 指定子があることに注意してください。

ここでは一般的なものにしましたが、 < MyType >の間に好きなものを入れることができます。ジェネリックとは、将来のプロジェクトを含め、1 つよりも多くの場所でこのことを使用できることを意味します。

于 2015-08-27T15:14:09.110 に答える
0

最終的には、開始時刻と終了時刻を事前に計算し、それらを ViewModel のプロパティとして追加するのがより簡単な解決策になると思います。特に、ビジネスロジックでこの値が必要になる可能性があると言っているからです。

于 2011-08-09T13:20:33.393 に答える