종속성 속성 값 우선 순위(WPF .NET)

WPF(Windows Presentation Foundation) 속성 시스템의 작동은 종속성 속성의 값에 영향을 줍니다. 이 문서에서는 WPF 속성 시스템 내에서 다양한 속성 기반 입력의 우선 순위가 종속성 속성의 유효 값을 결정하는 방법을 설명합니다.

중요

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

필수 구성 요소

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

WPF 속성 시스템

WPF 속성 시스템은 다양한 요소를 사용하여 관련 속성의 실시간 속성 유효성 검사, 지연 바인딩 및 속성 변경 알림과 같은 종속성 속성의 값을 결정합니다. 종속성 속성 값을 결정하는 데 사용되는 순서와 논리는 복잡하지만, 이를 알면 불필요한 속성 설정을 피하는 데 도움이 될 뿐 아니라 종속성 속성을 설정해도 예상 값이 발생하지 않은 이유를 파악할 수 있습니다.

여러 위치에서 설정되는 종속성 속성

다음 XAML 예제는 단추의 Background 속성에 대한 세 가지 "set" 작업이 해당 값에 어떤 영향을 줄 수 있는지 보여 줍니다.

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

이 예제에서 Background 속성은 로컬에서 Red로 설정됩니다. 그러나 단추의 범위에서 선언된 암시적 스타일은 Background 속성을 Blue로 설정하려고 시도합니다. 마우스가 단추 위에 있으면 암시적 스타일의 트리거가 Background 속성을 Yellow로 설정하려고 시도합니다. 강제 변환 및 애니메이션을 제외하고 로컬에서 설정된 속성 값이 우선 순위가 가장 높기 때문에 mouseover 시에도 단추는 빨간색이 됩니다. 그러나 로컬에서 설정된 값을 단추에서 제거하면 단추는 스타일에서 Background 값을 가져옵니다. 스타일 내에서는 트리거가 우선하므로 단추는 mouseover 시 노란색이 되고 그렇지 않으면 파란색이 됩니다. 기본 템플릿에 하드 코드된 mouseover Background 값이 있으므로 예제는 단추의 기본 ControlTemplate을 바꿉니다.

종속성 속성 우선 순위 목록

다음 목록은 런타임 값을 종속성 속성에 할당할 때 속성 시스템이 사용하는 확정적 우선 순위 순서입니다. 가장 높은 우선 순위가 가장 먼저 나열됩니다.

  1. 속성 시스템 강제 변환. 강제 변환에 대한 자세한 내용은 강제 변환 및 애니메이션을 참조하세요.

  2. 활성 애니메이션 또는 보류 동작이 있는 애니메이션. 실제적인 효과를 주려면 애니메이션 값이 기준(애니메이션 효과를 주지 않은) 값보다 우선해야 합니다. 기준 값이 로컬에서 설정된 경우에도 마찬가지입니다. 자세한 내용은 강제 변환 및 애니메이션을 참조하세요.

  3. 로컬 값. XAML에서 특성 또는 속성 요소를 설정하거나 특정 인스턴스의 속성을 사용한 SetValue API 메서드 호출과 같은 "래퍼" 속성을 통해 로컬 값을 설정할 수 있습니다. 바인딩 또는 리소스를 통해 설정된 로컬 값은 직접 설정된 값과 우선 순위가 동일합니다.

  4. TemplatedParent 템플릿 속성 값. 템플릿(ControlTemplate 또는 DataTemplate)에 의해 생성된 요소에는 TemplatedParent가 있습니다. 자세한 내용은 TemplatedParent를 참조하세요. TemplatedParent에 의해 지정된 템플릿 내에서 우선 순위는 다음과 같습니다.

    1. 트리거

    2. 속성 집합(일반적으로 XAML 특성을 통해)

  5. 암시적 스타일 Style 속성에만 적용됩니다. Style값은 요소 형식과 일치하는 TargetType 값이 있는 스타일 리소스입니다. 스타일 리소스는 페이지 또는 애플리케이션 내에 있어야 합니다. 암시적 스타일 리소스에 대한 조회는 테마의 스타일 리소스로 확장되지 않습니다.

  6. 스타일 트리거. 스타일 트리거는 명시적 또는 암시적 스타일 내의 트리거입니다. 스타일은 페이지 또는 애플리케이션 내에 있어야 합니다. 기본 스타일의 트리거는 우선 순위가 낮습니다.

  7. 템플릿 트리거. 템플릿 트리거는 직접 적용된 템플릿 또는 스타일 내에 있는 템플릿의 트리거입니다. 스타일은 페이지 또는 애플리케이션 내에 있어야 합니다.

  8. 스타일 setter 값. 스타일 setter 값은 스타일 내의 Setter에 의해 적용된 값입니다. 스타일은 페이지 또는 애플리케이션 내에 있어야 합니다.

  9. 기본 스타일, 즉 테마 스타일. 자세한 내용은 기본(테마) 스타일을 참조하세요. 기본 스타일 내에서 우선 순위는 다음과 같습니다.

    1. 활성 트리거.

    2. Setter.

  10. 상속. 자식 요소의 일부 종속성 속성은 부모 요소에서 그 값을 상속합니다. 따라서 애플리케이션 전체에서 모든 요소에 속성 값을 설정할 필요가 없을 수도 있습니다. 자세한 내용은 속성 값 상속을 참조하세요.

  11. 종속성 속성 메타데이터의 기본값 종속성 속성은 속성의 속성 시스템 등록 중에 기본값이 설정될 수 있습니다. 종속성 속성을 상속하는 파생 클래스는 형식별로 종속성 속성 메타데이터(기본값 포함)를 재정의할 수 있습니다. 자세한 내용은 종속성 속성 메타데이터를 참조하세요. 상속된 속성의 경우 부모 요소의 기본값이 자식 요소의 기본값보다 우선합니다. 따라서 상속 가능한 속성이 설정되지 않은 경우 자식 요소의 기본값 대신 루트 또는 부모의 기본값이 사용됩니다.

TemplatedParent

TemplatedParent 우선 순위는 표준 애플리케이션 태그에서 직접 선언된 요소의 속성에는 적용되지 않습니다. TemplatedParent 개념은 템플릿 적용을 통해 생성된 시각적 트리 내의 자식 항목에만 존재합니다. 속성 시스템은 TemplatedParent에 의해 지정된 템플릿에서 요소의 속성 값을 검색할 때 해당 요소를 만든 템플릿을 검색합니다. TemplatedParent 템플릿의 속성 값은 일반적으로 요소에서 로컬로 설정된 값처럼 작동하지만 템플릿이 잠재적으로 공유되기 때문에 실제 로컬 값보다 우선 순위가 낮습니다. 자세한 내용은 TemplatedParent를 참조하세요.

스타일 속성

Style 속성을 제외한 모든 종속성 속성에 동일한 우선 순위가 적용됩니다. Style 속성은 자체에 스타일을 지정할 수 없다는 점에서 고유합니다. Style 속성을 강제 변환하거나 애니메이션 효과를 주는 것은 권장되지 않습니다(또한 Style 속성에 애니메이션 효과를 주려면 사용자 지정 애니메이션 클래스가 필요합니다). 따라서 모든 우선 순위 항목이 적용되지는 않습니다. Style 속성을 설정하는 방법은 다음 세 가지뿐입니다.

  • 명시적 스타일. 요소의 Style 속성이 직접 설정됩니다. Style 속성 값은 로컬 값인 것처럼 작동하며 우선 순위 목록의 항목 3과 우선 순위가 같습니다. 대부분의 시나리오에서 명시적 스타일은 인라인으로 정의되지 않고, 대신 리소스로 명시적으로 참조됩니다(예: Style="{StaticResource myResourceKey}").

  • 암시적 스타일. Style 요소의 속성이 직접 설정되지 않습니다. 대신 스타일은 페이지 또는 애플리케이션 내의 특정 수준에 있고 스타일이 적용되는 요소의 형식과 일치하는 리소스 키가 있을 때 적용됩니다(예: <Style TargetType="x:Type Button">). 형식은 정확히 일치해야 합니다. 예를 들어 <Style TargetType="x:Type Button">MyButtonButton에서 파생되더라도 MyButton 형식에 적용되지 않습니다. Style 속성 값의 우선 순위는 우선 순위 목록의 항목 5와 같습니다. DependencyPropertyHelper.GetValueSource 메서드를 호출하고, Style 속성을 전달하고, 결과에서 ImplicitStyleReference를 확인하여 암시적 스타일 값을 검색할 수 있습니다.

  • 기본 스타일, 즉 테마 스타일. Style 요소의 속성이 직접 설정되지 않습니다. 대신 WPF 프레젠테이션 엔진의 런타임 테마 평가에서 제공됩니다. 런타임 전에 Style 속성 값은 null입니다. Style 속성 값의 우선 순위는 우선 순위 목록의 항목 9와 같습니다.

기본(테마) 스타일

WPF와 함께 제공되는 모든 컨트롤에는 테마에 따라 달라질 수 있는 기본 스타일이 있으므로 기본 스타일을 테마 스타일이라고도 합니다.

ControlTemplate은 컨트롤의 기본 스타일 내에서 중요한 항목입니다. ControlTemplate는 스타일 Template 속성의 setter 값입니다. 기본 스타일에 템플릿이 포함되지 않은 경우 사용자 지정 스타일의 일부로 사용자 지정 템플릿이 없는 컨트롤에는 시각적인 모양이 전혀 없습니다. 템플릿은 컨트롤의 시각적 모양을 정의할 뿐만 아니라 템플릿의 시각적 트리에 있는 속성과 해당 컨트롤 클래스 간의 연결도 정의합니다. 각 컨트롤은 템플릿을 교체하지 않고 컨트롤의 시각적인 모양에 영향을 줄 수 있는 속성의 집합을 노출합니다. 예를 들어 ScrollBar 구성 요소인 Thumb 컨트롤의 기본 시각적 모양을 생각해 보세요.

Thumb 컨트롤에는 사용자 지정 가능한 특정 속성이 있습니다. Thumb 컨트롤의 기본 템플릿은 중첩된 Border 구성 요소 몇 개가 있는 기본 구조 또는 시각적 트리를 만들어 3D 가장자리의 모양을 만듭니다. 템플릿 내에서 Thumb 클래스에 의해 사용자 지정 가능하도록 되어 있는 속성은 TemplateBinding을 통해 노출됩니다. Thumb 컨트롤의 기본 템플릿에는 템플릿 바인딩을 Background 또는 BorderThickness 같은 속성과 공유하는 다양한 테두리 속성이 있습니다. 그러나 속성 또는 시각적 개체 배열의 값이 템플릿에서 하드 코드되거나 테마에서 직접 제공되는 값에 바인딩되는 경우 전체 템플릿을 바꿔야 해당 값을 변경할 수 있습니다. 일반적으로 속성이 템플릿 부모에서 제공되고 TemplateBinding에 의해 노출되지 않는 경우 속성 값을 대상으로 지정할 편리한 방법이 없으므로 스타일이 속성 값을 변경할 수 없습니다. 그러나 해당 속성은 적용된 템플릿의 속성 값 상속이나 기본값의 영향을 받을 수 있습니다.

기본 스타일은 그 정의에서 TargetType을 지정합니다. 런타임 테마 평가는 기본 스타일의 TargetType과 컨트롤의 DefaultStyleKey 속성을 대조합니다. 반면 암시적 스타일의 조회 동작은 컨트롤의 실제 형식을 사용합니다. 파생 클래스는 DefaultStyleKey 값을 상속하므로 연결된 스타일이 없을 수 있는 파생 요소는 기본 시각적 모양을 갖습니다. 예를 들어 MyButtonButton에서 파생되는 경우 MyButtonButton의 기본 템플릿을 상속합니다. 파생 클래스는 종속성 속성 메타데이터에서 DefaultStyleKey의 기본값을 재정의할 수 있습니다. 따라서 MyButton에 다른 시각적 개체 표현을 원하는 경우 MyButton에서 DefaultStyleKey의 종속성 속성 메타데이터를 재정의한 다음 MyButton 컨트롤을 사용하여 패키지할 템플릿을 포함한 관련 기본 스타일을 정의할 수 있습니다. 자세한 내용은 컨트롤 작성 개요를 참조하세요.

동적 리소스

동적 리소스 참조 및 바인딩 작업은 설정된 위치의 우선 순위를 갖습니다. 예를 들어 로컬 값에 적용된 동적 리소스의 우선 순위는 우선 순위 목록의 항목 3과 동일합니다. 또 다른 예로 기본 스타일 내의 속성 setter에 적용되는 동적 리소스 바인딩의 우선 순위는 우선 순위 목록의 항목 9와 동일합니다. 동적 리소스 참조 및 바인딩은 애플리케이션의 런타임 상태에서 값을 얻어야 하므로 지정된 속성의 속성 값 우선 순위를 결정하는 프로세스는 런타임까지 확장됩니다.

동적 리소스 참조는 기술적으로 속성 시스템의 일부가 아니며, 우선 순위 목록과 상호 작용하는 자체 조회 순서를 갖습니다. 기본적으로 동적 리소스 참조의 우선 순위는 요소, 페이지 루트, 애플리케이션, 테마, 시스템 순서입니다. 자세한 내용은 XAML 리소스를 참조하세요.

동적 리소스 참조 및 바인딩은 설정된 위치의 우선 순위를 갖지만 값은 지연됩니다. 이로 인한 결과 중 하나는 동적 리소스나 바인딩을 로컬 값에 설정하는 경우 로컬 값을 변경하면 동적 리소스나 바인딩이 완전히 대체된다는 것입니다. ClearValue 메서드를 호출하여 로컬에 설정된 값을 지우더라도 동적 리소스나 바인딩은 복원되지 않습니다. 실제로 동적 리소스 또는 바인딩이 있는 속성(리터럴 로컬 값 없음)에서 ClearValue를 호출하면 동적 리소스 또는 바인딩이 지워집니다.

SetCurrentValue

SetCurrentValue 메서드도 속성을 설정할 수 있는 방법이지만 우선 순위 목록에 없습니다. SetCurrentValue를 사용하여 이전 값의 소스를 덮어쓰지 않고 속성 값을 변경할 수 있습니다. 예를 들어 속성이 트리거에 의해 설정된 후 SetCurrentValue를 사용하여 다른 값을 할당하는 경우 다음 트리거 작업은 속성을 다시 트리거 값으로 설정합니다. 속성 값에 로컬 값의 우선 순위를 제공하지 않고 속성 값을 설정하려는 경우 항상 SetCurrentValue를 사용할 수 있습니다. 마찬가지로 SetCurrentValue를 사용하여 바인딩을 덮어쓰지 않고 속성 값을 변경할 수 있습니다.

강제 변환 및 애니메이션

강제 변환과 애니메이션은 모두 기준 값에서 작동합니다. 기준 값은 우선 순위가 가장 높은 종속성 속성 값으로, 항목 2에 도달할 때까지 우선 순위 목록에서 위로 올라가며 평가하여 결정됩니다.

애니메이션이 특정 동작의 FromTo 속성 값을 지정하지 않거나 완료될 때 애니메이션이 의도적으로 기준 값으로 되돌아가는 경우 기준 값은 애니메이션 값에 영향을 줄 수 있습니다. 이를 실제로 확인하려면 대상 값 샘플 애플리케이션을 실행하세요. 샘플에서 사각형 높이에 어떤 From 값과도 다른 초기 로컬 값을 설정해 봅니다. 샘플 애니메이션은 기준 값 대신 From 값을 사용하여 즉시 시작됩니다. StopFillBehavior로 지정하면 완료 시 애니메이션은 속성 값을 기준 값으로 다시 설정합니다. 애니메이션이 종료된 후에는 일반적 우선 순위가 기준 값 결정에 사용됩니다.

각각 우선 순위가 다른 여러 애니메이션을 단일 속성에 적용할 수 있습니다. WPF 프레젠테이션 엔진은 우선 순위가 가장 높은 애니메이션을 적용하는 대신 애니메이션이 정의된 방식과 애니메이션 값의 형식에 따라 애니메이션 값을 합성할 수 있습니다. 자세한 내용은 애니메이션 개요를 참조하세요.

강제 변환은 우선 순위 목록의 맨 위에 있습니다. 실행 중인 애니메이션에도 값 강제 변환이 적용됩니다. WPF의 일부 기존 종속성 속성에는 기본 제공 강제 변환이 있습니다. 사용자 지정 종속성 속성의 경우 속성을 만들 때 메타데이터의 일부로 전달하는 CoerceValueCallback을 작성하여 강제 변환 동작을 정의할 수 있습니다. 파생 클래스에서 해당 속성의 메타데이터를 재정의하여 기존 속성의 강제 변환 동작을 재정의할 수도 있습니다. 강제 변환은 강제 변환에 대한 제약 조건이 존재할 때 적용되는 방식으로 기준 값과 상호 작용하지만 기준 값은 계속 유지됩니다. 따라서 강제 변환의 제약 조건이 나중에 해제되면 강제 변환은 가능한 가장 가까운 값을 기준 값에 반환하고, 모든 제약 조건이 해제되는 즉시 속성에 강제 변환이 미치는 영향이 중단될 수 있습니다. 강제 변환 동작에 대한 자세한 내용은 종속성 속성 콜백 및 유효성 검사를 참조하세요.

트리거 동작

컨트롤은 해당 기본 스타일의 일부로 트리거 동작을 정의하는 경우가 많습니다. 컨트롤에 로컬 속성을 설정하면 이러한 트리거와 충돌하여 트리거가 사용자 기반 이벤트에 (시각적으로 또는 동작으로) 응답하지 못할 수 있습니다. 속성 트리거는 일반적으로 IsSelected 또는 IsEnabled와 같은 상태 속성을 제어하는 데 사용됩니다. 예를 들어 기본적으로 Button을 사용하지 않는 경우 테마 스타일 트리거(IsEnabledfalse)는 Button이 회색으로 표시되도록 Foreground 값을 설정합니다. 로컬 Foreground 값을 설정한 경우 Button이 사용되지 않더라도 우선 순위가 높은 로컬 속성 값이 테마 스타일 Foreground 값을 무효화합니다. 컨트롤의 테마 수준 트리거 동작을 재정의하는 속성 값을 설정할 때는 해당 컨트롤에 대해 의도한 사용자 환경을 지나치게 방해하지 않도록 주의해야 합니다.

ClearValue

ClearValue 메서드는 요소에 대한 종속성 속성의 로컬로 적용된 값을 모두 지웁니다. 그러나 ClearValue를 호출한다고 해서 속성을 등록할 때 메타데이터에 설정된 기본값이 새 유효 값으로 보장되는 것은 아닙니다. 우선 순위 목록의 다른 모든 참가자는 여전히 활성 상태이며, 로컬로 설정된 값만 제거됩니다. 예를 들어 테마 스타일이 있는 속성에서 ClearValue를 호출하면 테마 스타일 값이 메타데이터 기반 기본값이 아니라 새 값으로 적용됩니다. 속성 값을 등록된 메타데이터 기본값으로 설정하려면 종속성 속성 메타데이터를 쿼리하여 기본 메타데이터 값을 가져오고 로컬에서 SetValue 호출을 사용하여 속성 값을 설정합니다.

참고 항목