사용자 지정 종속성 속성(WPF .NET)

WPF(Windows Presentation Foundation) 애플리케이션 개발자 및 구성 요소 작성자는 사용자 지정 종속성 속성을 만들어 속성의 기능을 확장할 수 있습니다. CLR(공용 언어 런타임) 속성과 달리 종속성 속성은 스타일 지정, 데이터 바인딩, 상속, 애니메이션 및 기본값에 대한 지원을 추가합니다. Background, Width, Text는 WPF 클래스의 기존 종속성 속성의 예입니다. 이 문서에서는 사용자 지정 종속성 속성을 구현하는 방법을 설명하고, 성능, 유용성, 다목적성을 개선하는 옵션을 설명합니다.

중요

.NET 7 및 .NET 6에 관한 데스크톱 가이드 설명서는 제작 중입니다.

필수 구성 요소

이 문서에서는 독자들이 종속성 속성에 대한 기본 지식을 갖고 있으며 종속성 속성 개요를 읽었다고 가정합니다. XAML(Extensible Application Markup Language)에 익숙하고 WPF 애플리케이션을 작성하는 방법을 알고 있으면 이 문서의 예제를 따라 하는 데 도움이 됩니다.

종속성 속성 식별자

종속성 속성은 Register 또는 RegisterReadOnly 호출을 통해 WPF 속성 시스템에 등록되는 속성입니다. Register 메서드는 종속성 속성의 등록된 이름과 특성을 보유하는 DependencyProperty 인스턴스를 반환합니다. 규칙에 따라 이름이 <property name>Property로 지정되는 종속성 속성 식별자라는 정적 읽기 전용 필드에 DependencyProperty 인스턴스를 할당합니다. 예를 들어 Background 속성의 식별자 필드는 항상 BackgroundProperty입니다.

종속성 속성 식별자는 프라이빗 필드를 사용하여 속성을 지원하는 표준 패턴이 아니라 속성 값을 얻거나 설정하기 위한 지원 필드로 사용됩니다. 속성 시스템뿐 아니라 XAML 프로세서도 식별자를 사용할 수 있으며, 코드(및 외부 코드)는 해당 식별자를 통해 종속성 속성에 액세스할 수 있습니다.

종속성 속성은 DependencyObject 형식에서 파생된 클래스에만 적용할 수 있습니다. DependencyObject가 WPF 클래스 계층 구조의 루트에 가깝기 때문에 대부분의 WPF 클래스는 종속성 속성을 지원합니다. 종속성 속성과 종속성 속성을 설명하는 데 사용된 용어 및 규칙에 대한 자세한 내용은 종속성 속성 개요를 참조하세요.

종속성 속성 래퍼

연결된 속성이 아닌 WPF 종속성 속성은 getset 접근자를 구현하는 CLR 래퍼에 의해 노출됩니다. 종속성 속성의 소비자는 속성 래퍼를 사용하여 다른 CLR 속성처럼 종속성 속성 값을 가져오거나 설정할 수 있습니다. getset 접근자는 DependencyObject.GetValueDependencyObject.SetValue 호출을 통해 기본 속성 시스템과 상호 작용하여 종속성 속성 식별자를 매개 변수로 전달합니다. 종속성 속성의 소비자는 일반적으로 GetValue 또는 SetValue를 직접 호출하지 않지만 사용자 지정 종속성 속성을 구현하는 경우 래퍼에서 해당 메서드를 사용합니다.

종속성 속성을 구현하는 경우

DependencyObject에서 파생되는 클래스에서 속성을 구현하는 경우 DependencyProperty 식별자로 속성을 지원하여 종속성 속성으로 만듭니다. 종속성 속성을 만드는 것이 유용한지 여부는 시나리오에 따라 달라집니다. 일부 시나리오에서는 프라이빗 필드를 사용하여 속성을 지원하는 것으로 충분하지만 속성이 다음 WPF 기능 중 하나 이상을 지원하도록 하려면 종속성 속성을 구현하는 것이 좋습니다.

  • 스타일 내에서 설정할 수 있는 속성 자세한 내용은 스타일 및 템플릿을 참조하세요.

  • 데이터 바인딩을 지원하는 속성 데이터 바인딩 종속성 속성에 대한 자세한 내용은 두 컨트롤의 속성 바인딩을 참조하세요.

  • 동적 리소스 참조를 통해 설정할 수 있는 속성 자세한 내용은 XAML 리소스를 참조하세요.

  • 요소 트리의 부모 요소에서 해당 값을 자동으로 상속하는 속성 이를 위해 CLR 액세스를 위한 속성 래퍼를 만드는 경우에도 RegisterAttached를 사용하여 등록해야 합니다. 자세한 내용은 속성 값 상속을 참조하세요.

  • 애니메이션 효과를 줄 수 있는 속성 자세한 내용은 애니메이션 개요를 참조하세요.

  • 속성 값이 변경되는 경우 WPF 속성 시스템에서 알림 변경은 속성 시스템, 환경, 사용자 또는 스타일의 작업으로 인한 것일 수 있습니다. 속성은 속성 시스템에서 속성 값이 변경되었다고 결정할 때마다 호출되는 속성 메타데이터에서 콜백 메서드를 지정할 수 있습니다. 관련 개념은 속성 값 강제 변환입니다. 자세한 내용은 종속성 속성 콜백 및 유효성 검사를 참조하세요.

  • WPF 프로세스에서 읽는 종속성 속성 메타데이터에 대한 액세스 예를 들어 속성 메타데이터를 사용하여 다음을 수행할 수 있습니다.

    • 변경된 종속성 속성 값에서 레이아웃 시스템이 요소의 시각적 개체를 다시 구성해야 하는지 여부를 지정합니다.

    • 파생 클래스에서 메타데이터를 재정의하여 종속성 속성의 기본값을 설정합니다.

  • 속성 창에서 사용자 지정 컨트롤의 속성을 편집하는 것과 같은 Visual Studio WPF 디자이너 지원. 자세한 내용은 컨트롤 작성 개요를 참조하세요.

일부 시나리오에서는 기존 종속성 속성의 메타데이터를 재정의하는 것이 새 종속성 속성을 구현하는 것보다 더 나은 옵션입니다. 메타데이터 재정의가 실용적인지 여부는 시나리오 및 해당 시나리오가 기존 WPF 종속성 속성 및 클래스의 구현과 얼마나 유사한지에 따라 달라집니다. 기존 종속성 속성에서 메타데이터 재정의에 대한 자세한 내용은 종속성 속성 메타데이터를 참조하세요.

종속성 속성을 만들기 위한 검사 목록

다음 단계에 따라 종속성 속성을 만듭니다. 일부 단계는 한 줄의 코드로 결합하고 구현할 수 있습니다.

  1. (선택 사항) 종속성 속성 메타데이터를 만듭니다.

  2. 속성 이름, 소유자 형식, 속성 값 형식 및 속성 메타데이터(선택 사항)를 지정하여 속성 시스템에 종속성 속성을 등록합니다.

  3. DependencyProperty 식별자를 소유자 형식의 public static readonly 필드로 정의합니다. 식별자 필드 이름은 Property 접미사가 추가된 속성 이름입니다.

  4. 종속성 속성 이름과 동일한 이름으로 CLR 래퍼 속성을 정의합니다. CLR 래퍼에서 래퍼를 지원하는 종속성 속성과 연결되는 getset 접근자를 구현합니다.

속성 등록

속성이 종속성 속성이 되려면 속성 시스템에 등록해야 합니다. 속성을 등록하려면 클래스 본문 내부(단, 멤버 정의 외부)에서 Register 메서드를 호출합니다. Register 메서드는 속성 시스템 API를 호출할 때 사용할 고유한 종속성 속성 식별자를 반환합니다. 멤버 정의 외부에서 Register 호출이 이루어지는 이유는 반환 값을 DependencyProperty 형식의 public static readonly 필드에 할당하기 때문입니다. 클래스에서 만들 이 필드는 종속성 속성의 식별자입니다. 다음 예에서 Register 이름의 첫 번째 인수는 종속성 속성 AquariumGraphic입니다.

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

참고

클래스 본문에서 종속성 속성을 정의하는 것이 일반적인 구현이지만 클래스 정적 생성자에서 종속성 속성을 정의할 수도 있습니다. 이 접근 방식은 종속성 속성을 초기화하기 위해 두 줄 이상의 코드가 필요한 경우에 유용할 수 있습니다.

종속성 속성 이름 지정

종속성 속성의 설정된 명명 규칙은 속성 시스템의 정상적인 동작에 필수적입니다. 만드는 식별자 필드의 이름은 접미사 Property가 있는 속성의 등록된 이름이어야 합니다.

종속성 속성 이름은 등록 클래스 내에서 고유해야 합니다. 기본 형식을 통해 상속되는 종속성 속성은 이미 등록되어 있으며 파생 형식이 등록할 수 없습니다. 그러나 클래스를 종속성 속성의 소유자로 추가하면 클래스가 상속하지 않는 형식이더라도 다른 형식이 등록한 종속성 속성을 사용할 수 있습니다. 클래스를 소유자로 추가하는 방법에 대한 자세한 내용은 종속성 속성 메타데이터를 참조하세요.

속성 래퍼 구현

규칙에 따라 래퍼 속성의 이름은 종속성 속성 이름인 Register 호출의 첫 번째 매개 변수와 동일해야 합니다. 래퍼 구현은 get 접근자에서 GetValue를 호출하고, set 접근자에서 SetValue를 호출합니다(읽기-쓰기 속성의 경우). 다음 예제는 등록 호출 및 식별자 필드 선언 후 래퍼를 보여 줍니다. WPF 클래스의 모든 공용 종속성 속성은 비슷한 래퍼 모델을 사용합니다.

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );

// Declare a read-write property wrapper.
public Uri AquariumGraphic
{
    get => (Uri)GetValue(AquariumGraphicProperty);
    set => SetValue(AquariumGraphicProperty, value);
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

' Declare a read-write property wrapper.
Public Property AquariumGraphic As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set
        SetValue(AquariumGraphicProperty, Value)
    End Set
End Property

드문 경우를 제외하고 래퍼 구현에는 GetValueSetValue 코드만 포함되어야 합니다. 그 이유는 사용자 지정 종속성 속성의 의미를 참조하세요.

속성이 설정된 명명 규칙을 따르지 않으면 다음과 같은 문제가 발생할 수 있습니다.

  • 스타일 및 템플릿의 일부 측면이 작동하지 않습니다.

  • 대부분의 도구와 디자이너는 명명 규칙을 사용하여 XAML을 올바르게 직렬화하고 속성별 수준에서 디자이너 환경 지원을 제공합니다.

  • WPF XAML 로더의 현재 구현은 래퍼를 완전히 무시하고 명명 규칙을 사용하여 특성 값을 처리합니다. 자세한 내용은 XAML 로딩 및 종속성 속성을 참조하세요.

종속성 속성 메타데이터

종속성 속성을 등록하면 속성 시스템은 속성 특성을 저장하는 메타데이터 개체를 만듭니다. Register 메서드의 오버로드를 사용하면 등록하는 동안 속성 메타데이터를 지정할 수 있습니다(예: Register(String, Type, Type, PropertyMetadata)). 속성 메타데이터의 일반적인 용도는 종속성 속성을 사용하는 새 인스턴스의 사용자 지정 기본값을 적용하는 것입니다. 속성 메타데이터를 제공하지 않으면 속성 시스템은 많은 종속성 속성 특성에 기본값을 할당합니다.

FrameworkElement에서 파생된 클래스에 종속성 속성을 만드는 경우 기본 클래스인 PropertyMetadata가 아닌 보다 특수화된 메타데이터 클래스인 FrameworkPropertyMetadata를 사용할 수 있습니다. 여러 FrameworkPropertyMetadata 생성자 서명을 사용하면 메타데이터 특성의 다양한 조합을 지정할 수 있습니다. 기본값만 지정하려면 FrameworkPropertyMetadata(Object)를 사용하고 기본값을 Object 매개 변수에 전달합니다. 값 형식이 propertyTypeRegister 호출에서 지정된 형식과 일치하는지 확인합니다.

일부 FrameworkPropertyMetadata 오버로드를 사용하면 속성에 메타데이터 옵션 플래그를 지정할 수 있습니다. 속성 시스템은 이러한 플래그를 불연속 속성으로 변환하고 플래그 값은 레이아웃 엔진과 같은 WPF 프로세스에서 사용됩니다.

메타데이터 플래그 설정

메타데이터 플래그를 설정하는 경우 다음을 고려하세요.

  • 속성 값(또는 속성 값의 변경)이 레이아웃 시스템이 UI 요소를 렌더링하는 방법에 영향을 주는 경우 다음 플래그 중 하나 이상을 설정합니다.

    • AffectsMeasure. 속성 값의 변경에 UI 렌더링, 특히 부모 내의 개체가 차지하는 공간의 변경이 필요함을 나타냅니다. 예를 들어 Width 속성에 이 메타데이터 플래그를 설정합니다.

    • AffectsArrange. 속성 값의 변경에 UI 렌더링, 특히 부모 내 개체의 위치 변경이 필요함을 나타냅니다. 일반적으로 개체의 크기는 변경되지 않습니다. 예를 들어 Alignment 속성에 이 메타데이터 플래그를 설정합니다.

    • AffectsRender. 레이아웃과 측정값에 영향을 주지 않지만 여전히 다른 렌더링이 필요한 변경이 발생했음을 나타냅니다. 예를 들어 Background 속성 또는 요소의 색상에 영향을 주는 다른 속성에 이 플래그를 설정합니다.

    이러한 플래그를 속성 시스템(또는 레이아웃) 콜백의 재정의 구현에 대한 입력으로 사용할 수도 있습니다. 예를 들어 인스턴스의 속성이 값 변경을 보고하고 메타데이터에 AffectsArrange가 설정된 경우 OnPropertyChanged 콜백을 사용하여 InvalidateArrange를 호출할 수 있습니다.

  • 일부 속성은 다른 방법으로 부모 요소의 렌더링 특성에 영향을 미칩니다. 예를 들어 MinOrphanLines 속성을 변경하면 흐름 문서의 전체 렌더링이 변경될 수 있습니다. 자체 속성에서 부모 작업 신호를 보내려면 AffectsParentArrange 또는 AffectsParentMeasure를 사용하세요.

  • 기본적으로 종속성 속성은 데이터 바인딩을 지원합니다. 하지만 실제 시나리오가 없거나 큰 개체에서처럼 데이터 바인딩 성능에 문제가 있는 경우 IsDataBindingAllowed를 사용하여 데이터 바인딩을 사용하지 않도록 설정할 수 있습니다.

  • 종속성 속성의 기본 데이터 바인딩 모드OneWay이지만 특정 바인딩의 바인딩 모드를 TwoWay로 변경할 수 있습니다. 자세한 내용은 바인딩 방향을 참조하세요. 종속성 속성 작성자는 양방향 바인딩이 기본 모드로 되도록 선택할 수도 있습니다. 양방향 데이터 바인딩을 사용하는 기존 종속성 속성의 예는 MenuItem.IsSubmenuOpen이며, 다른 속성 및 메서드 호출을 기반으로 하는 상태를 갖습니다. IsSubmenuOpen의 시나리오는 설정 논리 및 MenuItem의 구성이 기본 테마 스타일과 상호 작용한다는 것입니다. TextBox.Text는 기본적으로 양방향 바인딩을 사용하는 또 다른 WPF 종속성 속성입니다.

  • Inherits 플래그를 설정하여 종속성 속성에 속성 상속을 사용하도록 설정할 수 있습니다. 속성 상속은 부모 및 자식 요소에 공통 속성이 있고 자식 요소가 공통 속성의 부모 값을 상속하는 것이 적합한 시나리오에 유용합니다. 상속 가능한 속성의 예는 데이터 프레젠테이션에 마스터 세부 정보 시나리오를 사용하는 바인딩 작업을 지원하는 DataContext입니다. 속성 값 상속을 사용하면 페이지 또는 애플리케이션 루트에서 데이터 컨텍스트를 지정할 수 있으므로 자식 요소 바인딩에 대해 데이터 컨텍스트를 지정할 필요가 없습니다. 상속된 속성 값이 기본값을 재정의하지만 모든 자식 요소에서 속성 값을 로컬로 설정할 수 있습니다. 속성 값 상속은 성능 비용이 듭니다. 자주 사용하지 마세요. 자세한 내용은 속성 값 상속을 참조하세요.

  • 종속성 속성이 탐색 저널링 서비스에 의해 검색되거나 사용되어야 함을 나타내는 Journal 플래그를 설정합니다. 예를 들어 SelectedIndex 속성은 애플리케이션이 선택한 항목의 저널링 기록을 유지하도록 권장하는 Journal 플래그를 설정합니다.

읽기 전용 종속성 속성

읽기 전용인 종속성 속성을 정의할 수 있습니다. 일반적인 시나리오는 내부 상태를 저장하는 종속성 속성입니다. 예를 들어 IsMouseOver는 마우스 입력에 의해서만 상태가 결정되어야 하므로 읽기 전용입니다. 자세한 내용은 읽기 전용 종속성 속성을 참조하세요.

컬렉션 형식 종속성 속성

컬렉션 형식 종속성 속성에는 참조 형식에 대한 기본값 설정 및 컬렉션 요소에 대한 데이터 바인딩 지원과 같이 고려해야 할 추가 구현 문제가 있습니다. 자세한 내용은 컬렉션 형식 종속성 속성을 참조하세요.

종속성 속성 보안

일반적으로 종속성 속성을 공용 속성으로, DependencyProperty 식별자 필드를 public static readonly 필드로 선언합니다. protected와 같이 보다 제한적인 액세스 수준을 지정하는 경우 속성 시스템 API와 함께 식별자를 통해 종속성 속성에 계속 액세스할 수 있습니다. 보호된 식별자 필드도 WPF 메타데이터 보고 또는 값 결정 API(예: LocalValueEnumerator)를 통해 잠재적으로 액세스할 수 있습니다. 자세한 내용은 종속성 속성 보안을 참조하세요.

읽기 전용 종속성 속성의 경우 RegisterReadOnly에서 반환되는 값은 DependencyPropertyKey이며, 일반적으로 DependencyPropertyKey를 클래스의 public 멤버로 만들지 않습니다. WPF 속성 시스템은 코드 외부에 DependencyPropertyKey를 전파하지 않으므로 읽기 전용 종속성 속성의 set 보안이 읽기-쓰기 종속성 속성보다 좋습니다.

종속성 속성 및 클래스 생성자

일반적으로 코드 분석 도구에 의해 적용되는 관리 코드 프로그래밍에는 클래스 생성자가 가상 메서드를 호출해서는 안 된다는 일반적인 원칙이 있습니다. 파생 클래스 생성자를 초기화하는 동안 기본 생성자를 호출할 수 있고, 파생 클래스가 완전히 초기화되기 전에 기본 생성자에 의해 호출되는 가상 메서드가 실행될 수 있기 때문입니다. 이미 DependencyObject에서 파생된 클래스에서 파생하는 경우 속성 시스템 자체가 가상 메서드를 호출하고 내부적으로 노출합니다. 이러한 가상 메서드는 WPF 속성 시스템 서비스의 일부입니다. 메서드를 재정의하면 파생 클래스가 값 결정에 참여할 수 있습니다. 런타임 초기화와 관련된 잠재적인 문제를 방지하려면 특정 생성자 패턴을 따르지 않는 한 클래스의 생성자 내에서 종속성 속성 값을 설정하면 안 됩니다. 자세한 내용은 DependencyObjects의 안전한 생성자 패턴을 참조하세요.

참고 항목