다음을 통해 공유


컬렉션 형식 종속성 속성

이 항목에서는 속성 형식이 컬렉션 형식인 종속성 속성을 구현하는 방법에 대한 지침과 제안된 패턴을 제공합니다.

컬렉션 형식 종속성 속성 구현

일반적인 종속성 속성의 경우 필드 또는 다른 구문보다는 DependencyProperty 식별자에 기반하는 CLR 속성 래퍼를 정의하는 구현 패턴을 따릅니다. 콜렉션 형식 속성을 구현할 때도 이와 동일한 패턴을 따릅니다. 그러나 컬렉션에 포함된 형식 자체가 DependencyObject 또는 Freezable 파생 클래스인 경우 항상 컬렉션 형식 속성으로 인해 패턴이 약간 복잡해집니다.

기본값 이외의 컬렉션 초기화

종속성 속성을 만들 때는 속성 기본값을 초기 필드 값으로 지정하지 않습니다. 대신 종속성 속성 메타데이터를 통해 기본값을 지정합니다. 속성이 참조 형식인 경우 종속성 속성 메타데이터에 지정된 기본값은 인스턴스별 기본값이 아니라 해당 형식의 모든 인스턴스에 적용되는 기본값입니다. 따라서 컬렉션 속성 메타데이터로 정의된 단일 정적 컬렉션을 형식에 대해 새로 만든 인스턴스의 작업 기본값으로 사용하지 않도록 주의해야 합니다. 대신 클래스 생성자 논리의 일부로서 의도적으로 컬렉션 값을 고유한(인스턴스) 컬렉션으로 설정해야 합니다. 그렇지 않으면 의도하지 않은 Singleton 클래스가 만들어집니다.

다음 예제를 살펴보십시오. 예제의 다음 섹션은 기본값의 결함이 포함된 Aquarium 클래스의 정의를 보여 줍니다. 이 클래스는 FrameworkElement 형식 제약 조건과 함께 제네릭 List<T> 형식을 사용하는 컬렉션 형식 종속성 속성 AquariumObjects를 정의합니다. 종속성 속성에 대한 Register(String, Type, Type, PropertyMetadata) 호출에서 메타데이터는 기본값을 새로운 제네릭 List<T>으로 설정합니다.

경고

다음 코드는 올바르게 동작하지 않습니다.

public class Fish : FrameworkElement { }
public class Aquarium : DependencyObject {
    private static readonly DependencyPropertyKey AquariumContentsPropertyKey =
        DependencyProperty.RegisterReadOnly(
          "AquariumContents",
          typeof(List<FrameworkElement>),
          typeof(Aquarium),
          new FrameworkPropertyMetadata(new List<FrameworkElement>())
        );
    public static readonly DependencyProperty AquariumContentsProperty =
        AquariumContentsPropertyKey.DependencyProperty;

    public List<FrameworkElement> AquariumContents
    {
        get { return (List<FrameworkElement>)GetValue(AquariumContentsProperty); }
    }

    // ...
}
Public Class Fish
    Inherits FrameworkElement
End Class
Public Class Aquarium
    Inherits DependencyObject
    Private Shared ReadOnly AquariumContentsPropertyKey As DependencyPropertyKey = DependencyProperty.RegisterReadOnly("AquariumContents", GetType(List(Of FrameworkElement)), GetType(Aquarium), New FrameworkPropertyMetadata(New List(Of FrameworkElement)()))
    Public Shared ReadOnly AquariumContentsProperty As DependencyProperty = AquariumContentsPropertyKey.DependencyProperty

    Public ReadOnly Property AquariumContents() As List(Of FrameworkElement)
        Get
            Return CType(GetValue(AquariumContentsProperty), List(Of FrameworkElement))
        End Get
    End Property

    ' ...

End Class

그러나 설명한 대로 코드를 방금 벗어나면 모든 Aquarium 인스턴스에서 단일 목록 기본값을 공유합니다. 두 개의 개별 Aquarium 인스턴스를 인스턴스화하고 각 인스턴스에 서로 다른 단일 Fish를 추가하는 방법을 보여 주는 다음 테스트 코드를 실행하면 놀라운 결과를 볼 수 있습니다.

Aquarium myAq1 = new Aquarium();
Aquarium myAq2 = new Aquarium();
Fish f1 = new Fish();
Fish f2 = new Fish();
myAq1.AquariumContents.Add(f1);
myAq2.AquariumContents.Add(f2);
MessageBox.Show("aq1 contains " + myAq1.AquariumContents.Count.ToString() + " things");
MessageBox.Show("aq2 contains " + myAq2.AquariumContents.Count.ToString() + " things");
Dim myAq1 As New Aquarium()
Dim myAq2 As New Aquarium()
Dim f1 As New Fish()
Dim f2 As New Fish()
myAq1.AquariumContents.Add(f1)
myAq2.AquariumContents.Add(f2)
MessageBox.Show("aq1 contains " & myAq1.AquariumContents.Count.ToString() & " things")
MessageBox.Show("aq2 contains " & myAq2.AquariumContents.Count.ToString() & " things")

각 콜렉션의 개수는 1이 아니라 2입니다! 이는 메타데이터의 단일 생성자 호출로 인해 각 Aquarium에서 해당 Fish를 기본값 컬렉션에 추가했기 때문이며, 이에 따라 모든 인스턴스 간에 공유됩니다. 이 상황은 결코 바람직한 작업이 아닙니다.

이 문제를 해결하려면 클래스 생성자 호출의 일부로 컬렉션 종속성 속성 값을 고유한 인스턴스로 다시 설정해야 합니다. 속성이 읽기 전용 종속성 속성이므로 SetValue(DependencyPropertyKey, Object) 메서드를 사용하여 설정하고 클래스 안에서만 액세스할 수 있는 DependencyPropertyKey를 사용합니다.

public Aquarium() : base()
{
    SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>());
}
Public Sub New()
    MyBase.New()
    SetValue(AquariumContentsPropertyKey, New List(Of FrameworkElement)())
End Sub

이제 동일한 테스트 코드를 다시 실행하면 각 Aquarium에서 자체의 고유한 컬렉션을 지원하는 예상 결과를 더 많이 볼 수 있습니다.

읽기/쓰기 컬렉션 속성이 되도록 선택한 경우 이 패턴에 약간의 변형이 있습니다. 이 경우 초기화를 수행할 생성자에서 public set 접근자를 호출할 수 있습니다. 이 접근자는 공용 DependencyProperty 식별자를 사용하여 설정한 래퍼 내에서 SetValue(DependencyProperty, Object)의 키가 아닌 서명을 호출합니다.

컬렉션 속성에서 바인딩 값 변경 보고

자체적으로 종속성 속성인 콜렉션 속성은 변경 내용을 하위 속성에 자동으로 보고하지 않습니다. 컬렉션에 바인딩을 만들면 바인딩에서 변경 내용을 보고하지 못하도록 하여 일부 데이터 바인딩 시나리오가 무효화될 수 있습니다. 그러나 컬렉션 형식으로 FreezableCollection<T> 컬렉션 형식을 사용하면 컬렉션에 포함된 요소에 대한 하위 속성 변경 내용이 제대로 보고되고 바인딩이 예상대로 작동합니다.

종속성 개체 컬렉션에서 하위 속성 바인딩을 사용하려면 모든 DependencyObject 파생 클래스에 대한 해당 컬렉션의 형식 제약 조건을 사용하여 FreezableCollection<T> 형식으로 컬렉션 속성을 만듭니다.

참고 항목