2011-08-09 8 views
4

내 WPF 앱에서 항목 값이 이전 항목 에 따라 달라지는 ItemsControl이으로 표시됩니다.Observable LinkedList

ViewModel은 가변 길이 부분으로 분할 된 오디오 파일이며 오른쪽에 DateTime이 표시되는 방식으로 표시해야합니다. 그 값은 계산해야합니다 (각 부분의 길이 만 알고 있습니다. 내가 시작하고 끝나는 실제 시간과 ItemsControl에 위치를 계산해야합니다).

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

내 첫 번째 방법은 ObservableCollection<MyviewModel>을 사용했지만 곧 약간의 공포가 발생했습니다 :

돌아가 그에게의 DataContext의 속성을 설정 IMultiValueConverter 내가 값을 계산하는 것입니다있는

5 방향 multibinding 왜냐하면 나는 단지 런타임에 이전 요소를 알고 있었기 때문이다.

이전 요소는 Relativesource.PreviousData에 바인딩을 사용하여 보냈습니다.

이제 내 문제는 변환기 (분명히 나쁜 것)에서 값을 설정하고 실제로 작동 시키면 정규 컬렉션에 해당 요소의 순서 개념이 없으므로 더 이상 나머지 부분의 중간에 오디오 파트를 추가하고 싶을 때 디스플레이가 엉망입니다.

더 많은 비즈니스 로직을 구현할 때이 변환기에서 계산되는 오디오 부분의 시작 및 끝 부분에 액세스해야 할 수 있으며 아직 표시되지 않은 경우에는 어떻게해야합니까?

그런 접근법은 여러 단계에서 잘못되었습니다.

내가 검색을 시작하고 대략 LinkedList에 대해 알아 냈습니다. 지금은 기본적으로 관찰 가능한 LinkedList의 인 클래스를 만들려고 해요 (I는 일반적으로 필요하지 않은) :

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 
    } 

그리고 문제의 핵심은 내가 메소드를 오버라이드 (override) 할 수 없다는 것입니다을 그 ... 컬렉션 (의 addFirst,의 addLast 등)를 수정, 그래서 내가 제대로 OnNotifyCollectionChanged 호출 할 수 없습니다

그래서 나는 이러한 각 방법에 대한 과부하를 만들 수 생각하고 있어요,하지만 매우 불쾌한 소리

...

간단히 말해서 : 나는 각 항목이 그 중 하나를 계산하기 위해 이전 항목의 세부 사항을 알고있는 일종의 컬렉션이 필요합니다. 자신의 속성.

실마리가 있습니까? 이것도 좋은 해결책인가?

감사합니다.

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 
    } 

편집 :

부록은 뷰 모델처럼 보이는 내가 대신 내 사용자 지정 개체에 LinkedList의 인스턴스를 유지 즉, 내가 (구성을 사용할 것이다 : 나는 토마스와 윌의 답변을 결합하려고 생각 그것으로부터 상속받은) 그리고 실제 LinkedList 메서드를 호출 한 후에 OnNotifyPropertychanged를 호출 할 AddAfter, AddFirst 등의 메소드를 재정의하십시오. 그것은 약간의 일이지만 내 문제에 대한 어떤 우아한 해결책도 없을 것이라고 생각합니다 ...

답변

4

이제는 IEnumerable을 지원하는 사용자 지정 제네릭 클래스를 만들었으며, 변경된 사항이 WPF에 통보되는 유일한 차이점을 가지고 LinkedList<T> 인 것처럼 사용됩니다.

이 솔루션은 합리적으로 작은 컬렉션에서만 작동하므로 최대 30 개의 요소를 관리해야하므로 나에게 도움이되지만이 컬렉션을 수정할 때마다 "재설정"으로 간주됩니다.

코멘트에 @AndrewS에 의해 했나요으로
/// <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 
    } 

, LinkedListNode이 목록 속성에서 ObservableLinkedList을 반환하는 사용자 정의 클래스로 대체해야합니다

여기에 솔루션을 간다.

이 같은 클래스를 만들었다 경우
+0

LinkedListNode 을 반환하는 메서드는 List 속성을 통해 래핑하려는 내부 LinkedList 을 공개적으로 노출하므로이 메서드를 제외 할 수 있습니다. 또는 대신 돌아올 수있는 자신 만의 클래스를 만드십시오. – AndrewS

+0

@AndrewS 왜 내가 그걸하고 싶습니까? 노드에 액세스 할 때 수행해야 할 작업은 무엇입니까? –

+0

내부 링크드 목록을 observablelinkedlist의 모든 사용자에게 공개하기 때문에 누군가가 직접 링크를 조작 할 수 있으며 콜렉션 변경 알림이 발생하지 않기 때문입니다. – AndrewS

2

LinkedList<T>은 상속을 위해 설계되지 않았습니다. 대부분의 메소드는 가상이 아니므로 대체 할 항목이 없습니다. 구현을 재사용하고 INotifyCollectionChanged을 구현하려면 상속이 아닌 합성을 사용하십시오.

어쨌든, 그것은 연결리스트는 인덱스에 의한 랜덤 액세스를 지원하지 않기 때문에, 관측 연결리스트를 구현하는 의미가 없습니다 것입니다, 당신은 인덱스를 지정하는 경우에만 NotifyCollectionChangedAction.Reset를 제기하지 않는 한 CollectionChanged 알림 (유용하다 알림,하지만 그다지 효율적이지는 않습니다.)

+0

NotifyCollectionChangedAction.Reset은 증서를 작성합니다. 어쨌든 20 개 이상의 세그먼트를 얻지는 않을 것입니다. 다른 단점은? 실제로 구독 할 것이라고 CollectionChanged 이벤트 OnNotifyCollectionChanged (NotifyCollectionChangedAction.Reset) 호출 그냥 생각 중입니다. –

+0

나는 다른 단점을 생각할 수 없다 ... 콜렉션이 작 으면 성능에 큰 영향을주지 않아야한다. –

1

좋은 해결책입니다. LinkedList의 구현을 직접 만들어야합니다.

LinkedList<T>은 연결이 가능한 인터페이스를 구현하지 않으므로 메서드/속성에 대해 직접 결정해야합니다. LinkedList<T>의 공용 메소드와 속성을 복제하는 것이 좋은 안내서라고 생각합니다. 이렇게하면 컬렉션의 실제 인스턴스를 백업 저장소로 사용할 수 있습니다.

+0

나는 수동으로 OnNotifyCollectionChanged를 수동으로 호출 할 필요가 없다. LinkedList의 메서드,하지만 내 생각에 나쁜 디자인을 사용하면 안되는 메서드를 남길 수 있습니다. –

+0

@Baboon : Simple. 메소드에서 발생하는 업데이트의 종류를 자세히 설명하는 사용자 정의 속성을 만든 다음 T4 템플릿을 사용하여 해당 속성으로 멤버가 장식 된 유형의 부분 클래스를 생성합니다. 글쎄, 그렇게 간단하지는 않지만 (클래스의 커스텀 TypeDescriptors를 생성하기 위해이 작업을 했음에도 불구하고 어렵다.) 불행하게도이 문제를 해결할 쉬운 방법이 없다고 생각합니다. 그러나 일단 당신이 그것을 썼다면, 당신은 그것을 반복해서 사용할 수 있습니다! – Will

0

결국보다 간단한 해결책은 미리 시작 시간과 종료 시간을 계산하여 ViewModel의 속성으로 추가하는 것입니다. 특히 비즈니스 로직에서이 값이 필요할 수도 있다고 말하기 때문에 말입니다.

+0

시작 시간과 종료 시간은 부품이 배치되는 위치에 따라 달라지며 매우 가변적입니다. –

+0

부품을 이동할 때 다시 계산할 수 있습니다. 내 말은 ... 특히 리셋 이벤트를 사용하려는 경우 변환기 및 바인딩을 사용할 때 어떤 일이 발생한다고 생각합니까? :) 당신의 목록을 반복하고 시작과 종료 시간을 설정하는 한 가지 방법은 새로운 컬렉션 클래스를 추가하고 여러 변환기를 사용하는 것보다 훨씬 간단하다고 말하고 싶습니다. – Bubblewrap

0

두 가지 문제가있는 것 같습니다. 하나는 표시 할 항목 목록을 관리하는 것이고 다른 하나는 항목이 선행 및 다음 항목에 액세스하는 것을 허용하는 것입니다.

그래서 접근 방법은 다음과 같습니다. PreviousNext 속성을 항목 클래스에 추가하고 컬렉션을 처음 채울 때 설정 한 다음 목록에서 항목을 삽입 및 제거 할 때 업데이트합니다. 당신이 정말로 너트를 가서 일반적인 솔루션을 확인하려면

, 당신은 ILinkedListNode 인터페이스를 구현할 수, 그리고 다양한 인서트를 무시 ObservableCollection<T> where T : ILinkedListNode를 서브 클래스 및 항목 'PreviousNext 속성을 업데이트하는 방법을 제거합니다. 솔루션을 재사용 할 필요가 있다면 그렇게 할 것입니다.

그렇다면 컬렉션을 래핑하고 Items 속성으로 노출 한 뷰 모델 클래스를 만든 다음 UI에서 바인딩 할 수있는 명령을 삽입 및 제거합니다.

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. 
} 
} 

당신은 새로운 키워드 메소드를 오버라이드 (override) 아무런 문제가 없어야합니다.
클래스 자체에는 연결된 목록 형식과 일치하는 TYPE 지정자가 있습니다.

여기에 일반적인 내용을 넣었으나 원하는 내용은 모두 < MyType> 사이에 넣을 수 있습니다. Generic은 미래 프로젝트를 포함하여 1보다 많은 장소에서이 것을 사용할 수 있다는 것을 의미합니다.

관련 문제