리소스 개요

업데이트: 2007년 11월

이 개요에서는 WPF 리소스를 간단한 방식으로 사용하여 일반적으로 정의된 개체 및 값을 다시 사용하는 방법을 설명합니다. 이 개요에서는 주로 XAML에서 리소스를 사용하는 방법을 설명합니다. 또한 코드를 사용하거나 코드와 XAML(Extensible Application Markup Language)을 서로 바꿔 사용하여 리소스를 만들고 액세스할 수 있습니다. 자세한 내용은 리소스 및 코드를 참조하십시오.

이 항목에는 다음 단원이 포함되어 있습니다.

  • XAML에서 리소스 사용
  • 정적 리소스와 동적 리소스
  • Style, DataTemplate 및 암시적 키
  • 관련 항목

XAML에서 리소스 사용

다음 예제에서는 페이지의 루트 요소에 SolidColorBrush를 리소스로 정의합니다. 그리고 나서 리소스를 참조하고 사용하여 Ellipse, TextBlock, Button 등의 여러 자식 요소 속성을 설정합니다.

<Page Name="root"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
>
  <Page.Resources>
    <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
    <Style TargetType="Border" x:Key="PageBackground">
      <Setter Property="Background" Value="Blue"/>
    </Style>
    <Style TargetType="TextBlock" x:Key="TitleText">
      <Setter Property="Background" Value="Blue"/>
      <Setter Property="DockPanel.Dock" Value="Top"/>
      <Setter Property="FontSize" Value="18"/>
      <Setter Property="Foreground" Value="#4E87D4"/>
      <Setter Property="FontFamily" Value="Trebuchet MS"/>
      <Setter Property="Margin" Value="0,40,10,10"/>
    </Style>
    <Style TargetType="TextBlock" x:Key="Label">
      <Setter Property="DockPanel.Dock" Value="Right"/>
      <Setter Property="FontSize" Value="8"/>
      <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
      <Setter Property="FontFamily" Value="Arial"/>
      <Setter Property="FontWeight" Value="Bold"/>
      <Setter Property="Margin" Value="0,3,10,0"/>
    </Style>
  </Page.Resources>
  <StackPanel>
    <Border Style="{StaticResource PageBackground}">
      <DockPanel>
        <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
        <TextBlock Style="{StaticResource Label}">Label</TextBlock>
        <TextBlock DockPanel.Dock="Top" HorizontalAlignment="Left" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
        <Button DockPanel.Dock="Top" HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
        <Ellipse DockPanel.Dock="Top" HorizontalAlignment="Left" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="40" />
      </DockPanel>
    </Border>
  </StackPanel>
</Page>


각 프레임워크 수준 요소(FrameworkElement 또는 FrameworkContentElement)에는 Resources 속성이 있습니다. 이 속성은 리소스가 정의하는 ResourceDictionary 리소스를 포함합니다. 어떤 요소에도 리소스를 정의할 수 있지만 대부분의 경우 리소스는 루트 요소에 정의됩니다. 예제에서는 Page가 루트 요소입니다.

리소스 사전의 각 리소스에는 고유 키가 있어야 합니다. 태그에 리소스를 정의하는 경우 x:Key 특성을 통해 고유 키를 할당합니다. 일반적으로 키는 문자열이지만 적합한 태그 확장을 사용하여 다른 개체 형식으로 설정할 수도 있습니다. 리소스에 대한 비문자열 키는 특별히 스타일, 구성 요소 리소스 및 데이터 스타일 지정을 위해 WPF의 특정 기능 영역에서 사용됩니다.

리소스를 정의한 후 키 이름을 지정하는 리소스 태그 확장 구문을 사용하여 속성 값에 사용할 리소스를 참조할 수 있습니다. 예를 들면 다음과 같습니다.

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

이 예제에서 XAML 로더가 ButtonBackground 속성에 대한 값 {StaticResource MyBrush}를 처리하는 경우 리소스 조회 논리는 먼저 Button 요소에 대한 리소스 사전을 검사합니다. Button에 리소스 키 MyBrush의 정의가 없는 경우(이 정의가 없으면 해당 리소스 컬렉션이 비워져 있음) 조회에서는 다음으로 Button의 부모 요소인Page를 검사합니다. 따라서 Page 루트 요소에 리소스를 정의하는 경우 Page의 논리 트리에 있는 모든 요소에서 해당 리소스에 액세스할 수 있으며 사용자는 리소스가 나타내는 Type을 사용하는 속성의 값을 설정하는 데 동일한 리소스를 다시 사용할 수 있습니다. 앞의 예제에서 동일한 MyBrush 리소스는 서로 다른 두 개의 속성인 ButtonBackgroundRectangleFill을 설정합니다.

정적 리소스와 동적 리소스

리소스는 정적 리소스 또는 동적 리소스로 참조할 수 있습니다. 이 작업은 StaticResource 태그 확장 또는 DynamicResource 태그 확장을 사용하여 수행합니다. 태그 확장은 XAML 기능이므로 사용자가 태그 확장에서 특성 문자열을 처리하고 개체를 XAML 로더에 반환하도록 설정하여 개체 참조를 지정할 수 있습니다. 태그 확장 동작에 대한 자세한 내용은 태그 확장 및 XAML을 참조하십시오.

태그 확장을 사용할 때 일반적으로 사용자는 설정되는 속성의 컨텍스트에서 평가되지 않고 해당 특정 태그 확장을 통해 처리되는 하나 이상의 매개 변수를 문자열 형식으로 제공합니다. StaticResource 태그 확장은 사용 가능한 모든 리소스 사전에서 해당 키에 대한 값을 조회하여 키를 처리합니다. 이러한 작업은 로드 시 수행되면 이 시점에서는 로드 프로세스가 정적 리소스 참조를 사용하는 속성 값을 할당할 필요가 있습니다. DynamicResource 태그 확장은 대신 식을 만들어 키를 처리하며 식은 식이 계산되어 값을 제공하는 시기인 응용 프로그램이 실제로 실행되는 시기에 도달할 때까지 계산되지 않습니다.

리소스를 참조할 때 다음 고려 사항은 사용자가 정적 리소스 참조를 사용할지, 아니면 동적 리소스 참조를 사용할지에 영향을 줄 수 있습니다.

  • 응용 프로그램의 리소스를 만드는 방법에 대한 전체 디자인. 이러한 디자인에는 리소스 전용 어셈블리에서 만드는 방법, 느슨한 XAML로 만드는 방법, 응용 프로그램에서 만드는 방법, 페이지마다 만드는 방법이 포함되어 있습니다.

  • 리소스를 실시간으로 업데이트하는 응용 프로그램 기능이 응용 프로그램 요구 사항에 포함되어 있는지 여부

  • 해당 리소스 참조 형식의 각 조회 동작

  • 특정 속성 또는 리소스 형식 및 이러한 형식의 기본 동작

정적 리소스

정적 리소스 참조는 다음 경우에 최적의 상태로 작동합니다.

  • 응용 프로그램 디자인에서 해당 리소스의 대부분을 페이지 또는 응용 프로그램 수준의 리소스 사전에 집중시키는 경우. 정적 리소스 참조는 페이지 다시 로드와 같은 런타임 동작을 기반으로 다시 평가되지 않으므로 리소스 및 응용 프로그램 디자인에서 필요하지 않은 많은 수의 동적 리소스 참조를 실행할 필요가 없어 성능이 약간 향상될 수 있습니다.

  • DependencyObject 또는 Freezable에 해당하지 않는 속성의 값을 설정하는 경우

  • DLL로 컴파일되고 응용 프로그램 일부로 패키징되거나 응용 프로그램 간에 공유되는 리소스 사전을 만드는 경우

  • 사용자 지정 컨트롤에 대한 테마를 만들고 해당 테마 내에 사용되는 리소스를 정의하는 경우. 이 경우에는 일반적으로 동적 리소스 참조 조회 동작을 원하지 않으며 대신 정적 리소스 참조 동작을 원하므로 조회를 예측할 수 있으며 조회가 테마에 독립적으로 포함됩니다. 동적 리소스 참조를 사용하면 테마 내의 참조도 런타임 이전에는 평가되지 않으며 테마가 적용되면 일부 로컬 요소가 테마가 참조하려는 키를 다시 정의하며 로컬 요소가 조회에서 자체 테마 앞에 배치될 수 있습니다. 이렇게 되면 테마가 예상되는 방식으로 작동하지 않게 됩니다.

  • 많은 수의 종속성 속성을 설정하는 리소스를 사용하는 경우. 종속성 속성에는 속성 시스템에 의해 활성화되는 유효 값 캐싱이 있으므로 사용자가 로드 시 평가할 수 있는 종속성 속성의 값을 제공하는 경우 종속성 속성이 다시 계산된 식을 검사하지 않아야 하며 마지막 유효 값을 반환할 수 있습니다. 이 기술로 인해 성능이 향상될 수 있습니다.

  • 모든 소비자에 대해 기본 리소스를 변경하거나 x:Shared 특성을 사용하여 각 소비자에 대해 별도의 쓰기 가능한 인스턴스를 유지 관리할 수 있습니다.

정적 리소스 조회 동작

  1. 조회 프로세스는 속성을 설정하는 요소에서 정의한 리소스 사전 내에서 요청된 키를 검사합니다.

  2. 그리고 나서 조회 프로세스에서 논리 트리를 부모 요소 및 해당 리소스 사전 방향으로 위로 이동합니다. 루트 요소에 도달할 때까지 계속 이동합니다.

  3. 다음으로 응용 프로그램 리소스를 검사합니다. 응용 프로그램 리소스는 WPF 응용 프로그램의 Application 개체에서 정의하는 리소스 사전 내에 있는 리소스입니다.

리소스 사전 내의 정적 리소스 참조는 리소스 참조 이전에 이미 구문적으로 정의된 리소스를 참조해야 합니다. 전방 참조는 정적 리소스 참조에서 확인할 수 없습니다. 따라서 정적 리소스 참조를 사용하는 경우 리소스에서의 사용을 위한 리소스가 각 해당 리소스 사전에 또는 시작 부분에 정의되도록 리소스 사전 구조를 디자인해야 합니다.

정적 리소스 조회는 테마 또는 시스템 리소스로 확장될 수 있지만 이는 단지 XAML 로더에서 요청을 지연하기 때문에 지원됩니다. 페이지가 로드할 때 런타임 테마가 응용 프로그램에 적절히 적용될 수 있도록 이 지연이 필요합니다. 그러나 단지 테마에 있거나 시스템 리소스로 존재한다고 알려진 키에 대한 정적 리소스 참조는 사용하지 않는 것이 좋습니다. 왜냐하면 테마가 실시간에 사용자에 의해 변경될 때 이러한 참조가 다시 평가되지 않기 때문입니다. 테마 또는 시스템 리소스를 요청할 때는 동적 리소스 참조가 더 안정적입니다. 단, 테마 요소 자체가 다른 리소스를 요청하는 경우는 예외입니다. 앞에서 언급한 이유 때문에 이러한 참조는 정적 리소스 참조여야 합니다.

정적 리소스 참조를 찾을 수 없는 경우의 예외 동작은 다양합니다. 리소스가 연기되면 런타임에 예외가 발생합니다. 리소스가 연기되지 않으면 로드 시 예외가 발생합니다.

동적 리소스

동적 리소스는 다음 경우에 최적의 상태로 작동합니다.

  • 리소스 값이 런타임 전에는 알 수 없는 조건에 따라 다른 경우. 여기에는 시스템 리소스 또는 다른 경우 사용자가 설정할 수 있는 리소스가 포함됩니다. 예를 들어 SystemColors, SystemFonts, 또는 SystemParameters에서 노출하는 대로 시스템 속성을 참조하는 setter 값을 만들 수 있습니다. 이러한 값은 궁극적으로 사용자 및 운영 체제의 런타임 환경에서 가져오기 때문에 실제로 동적이라고 말할 수 있습니다. 변경할 수 있는 응용 프로그램 수준 테마를 포함할 수 있는데 여기서는 페이지 수준 리소스 액세스도 변경 사항을 캡처해야 합니다.

  • 사용자 지정 컨트롤에 대한 테마 스타일을 만들거나 참조하는 경우

  • 응용 프로그램 수명 동안 ResourceDictionary의 내용을 조정하려는 경우

  • 상호 종속성을 포함하는 복잡한 리소스 구조가 있으며 여기에 전방 참조가 필요할 수 있는 경우. 정적 리소스 참조는 전방 참조를 지원하지 않지만 런타임 전에는 리소스를 평가할 필요가 없으므로 동적 리소스 참조에서는 이러한 전방 참조를 지원합니다. 따라서 전방 참조는 적합한 개념이 아닙니다.

  • 컴파일 또는 작업 집합 측면에서 매우 큰 리소스를 참조하고 있으며 페이지가 로드될 때 리소스가 즉시 사용되지 않을 수도 있는 경우. 페이지가 로드될 때 XAML에서 항상 정적 리소스 참조가 로드되지만 동적 리소스 참조는 실제로 사용되기 전에는 로드되지 않습니다.

  • setter 값을 테마 또는 다른 사용자 설정의 영향을 받는 다른 값에서 가져 올 수 있는 스타일을 만드는 경우

  • 응용 프로그램 수명 동안 논리 트리에서 부모가 재지정될 수 있는 요소에 리소스를 적용하는 경우. 부모를 변경하면 리소스 조회 범위가 변경될 수도 있으므로 부모가 재지정된 요소를 새 범위를 기반으로 다시 평가하기 위해 리소스가 필요한 경우에는 항상 동적 리소스 참조를 사용합니다.

동적 리소스 조회 동작

FindResource 또는 SetResourceReference를 호출하는 경우 동적 리소스 참조에 대한 리소스 조회 동작은 코드의 조회 동작과 유사합니다.

  1. 조회 프로세스는 속성을 설정하는 요소에서 정의한 리소스 사전 내에서 요청된 키를 검사합니다.

  2. 그리고 나서 조회 프로세스에서 논리 트리를 부모 요소 및 해당 리소스 사전 방향으로 위로 이동합니다. 루트 요소에 도달할 때까지 계속 이동합니다.

  3. 다음으로 응용 프로그램 리소스를 검사합니다. 응용 프로그램 리소스는 WPF 응용 프로그램의 Application 개체에서 정의하는 리소스 사전 내에 있는 리소스입니다.

  4. 현재 활성 테마에 대해 테마 리소스 사전을 검사합니다. 런타임에 테마가 변경되면 값이 다시 계산됩니다.

  5. 시스템 리소스를 검사합니다.

예외 동작(있는 경우)은 다음과 같이 상황에 따라 다릅니다.

  • FindResource 호출에 의해 리소스를 요청했지만 찾을 수 없는 경우 예외가 발생합니다.

  • TryFindResource 호출에 의해 리소스를 요청했지만 찾을 수 없는 경우 예외가 발생하지 않지만 null 값이 반환됩니다. 설정되는 속성에서 null을 사용하지 않는 경우에는 깊은 수준의 예외가 발생할 수 있습니다. 이는 설정되는 개별 속성에 따라 다릅니다.

  • XAML의 동적 리소스 참조에 의해 리소스를 요청했지만 찾을 수 없는 경우 동작은 일반 속성 시스템에 따라 다르지만 일반적인 동작은 리소스가 있는 수준에서 속성 설정 작업이 발생하지 않은 것과 같습니다. 예를 들어 평가할 수 없는 리소스를 사용하여 개별 단추 요소에 배경을 설정하려는 경우 결과 값 집합이 없지만 속성 시스템 및 값 우선 순위의 다른 참가 요소에서 유효 값을 계속 가져올 수 있습니다. 예를 들어 로컬로 정의된 단추 스타일 또는 테마 스타일에서 배경 값을 계속 가져올 수 있습니다. 테마 스타일에서 정의하지 않은 속성의 경우 실패한 리소스 평가 이후의 유효 값은 속성 메타데이터의 기본값에서 가져올 수 있습니다.

제한

동적 리소스 참조에는 몇 가지 주의할 제한이 있습니다. 다음 제한 중 하나 이상이 적용되어야 합니다.

설정되는 속성은 DependencyProperty 또는 Freezable 속성이어야 하므로 대부분의 속성 변경 사항은 UI로 전파됩니다. 변경된 동적 리소스 값인 속성 변경 사항은 속성 시스템에 의해 승인됩니다. DependencyProperty 변경 사항 및 해당 속성이 레이아웃에 영향을 줄 수 있는 경우 대부분의 컨트롤에는 컨트롤의 다른 레이아웃을 강제로 적용하는 논리가 포함되어 있습니다. 그러나 DynamicResource 태그 확장을 값으로 포함하는 속성이 모두 UI에 실시간으로 업데이트되는 방식으로 값을 제공하는 것은 아닙니다. 이 기능은 속성, 속성을 소유하는 형식 또는 응용 프로그램의 논리 구조에 따라 달라질 수 있습니다.

Style, DataTemplate 및 암시적 키

앞부분에서 ResourceDictionary의 모든 항목에는 키가 있어야 한다고 설명했습니다. 그렇다고 모든 리소스에 명시적 x:Key가 있어야 한다는 의미는 아닙니다. 여러 개체 형식은 리소스로 정의된 경우 암시적 키를 지원하지만 이 경우 키 값은 다른 속성의 값에 연결되어 있습니다. 이를 암시적 키라고 하지만 x:Key 특성은 명시적 키입니다. 명시적 키를 지정하여 암시적 키를 덮어쓸 수 있습니다.

리소스에 대한 한 가지 매우 중요한 시나리오는 Style을 정의하는 경우입니다. 사실 스타일은 기본적으로 다시 사용하기 위해 디자인된 것이므로 Style은 항상 리소스 사전에 엔트리로 정의됩니다. 스타일에 대한 자세한 내용은 스타일 지정 및 템플릿를 참조하십시오.

컨트롤에 대한 스타일은 모두 암시적 키와 함께 생성되고 참조될 수 있습니다. 컨트롤의 기본 모양을 정의하는 테마 스타일은 이 암시적 키를 사용합니다. 암시적 키는 컨트롤을 요청하는 관점에서 볼 때 자체 컨트롤의 Type이며, 리소스를 정의하는 관점에서 볼 때는 스타일의 TargetType입니다. 따라서 사용자 지정 컨트롤에 대한 테마를 만들고 기존 테마 스타일과 상호 작용하는 스타일을 만드는 경우 해당 Stylex:Key 특성를 지정할 필요가 없습니다. 그리고 테마가 적용된 스타일을 사용할 경우 스타일을 지정할 필요가 없습니다. 예를 들어 Style 리소스에 키가 없는 것처럼 보여도 다음 스타일 정의가 작동합니다.

<Style TargetType="Button">
  <Setter Property="Background">
    <Setter.Value>
      <LinearGradientBrush>
        <GradientStop Offset="0.0" Color="AliceBlue"/>
        <GradientStop Offset="1.0" Color="Salmon"/>           
      </LinearGradientBrush>
    </Setter.Value>
  </Setter>  
  <Setter Property="FontSize" Value="18"/>
</Style>

해당 스타일에는 실제로 암시적 키 typeof(Button)인 키가 없습니다. 태그에서 TargetType을 직접 형식 이름으로 지정하거나 {x:Type...}을 사용하여 Type을 반환할 수 있습니다.

WPF에서 사용하는 기본 테마 스타일 메커니즘을 통해 해당 스타일은 Button 자체가 해당 Style 속성 또는 해당 스타일에 대한 특정 리소스 참조를 지정하지 않으려는 경우에도 페이지에 Button의 런타임 스타일로 적용됩니다. 페이지에 정의된 스타일은 테마 사전 스타일에 있는 동일한 키를 사용하여 테마 사전 스타일보다 이전 조회 시퀀스에서 먼저 발견됩니다. 페이지의 어느 위치에나 <Button>Hello</Button>를 지정할 수 있으며 Button의 TargetType을 사용하여 정의한 스타일이 해당 단추에 적용됩니다. 원하는 경우 태그에서 보다 명확하게 표현하기 위해 TargetType(선택 사항)과 같은 형식 값을 사용하여 스타일에 키를 명시적으로 지정할 수 있습니다.

스타일에 대한 암시적 키는 OverridesDefaultStyle이 true인 경우 컨트롤에 적용되지 않습니다. 여기서 OverridesDefaultStyle은 컨트롤 인스턴스에서 명시적이지 않고 컨트롤 클래스에 대한 기본 동작의 일부로 설정될 수 있다는 사실에 유념하십시오. 또한 파생된 클래스 시나리오에 대한 암시적 키를 지원하기 위해 컨트롤에서는 DefaultStyleKey를 재정의해야 합니다. WPF의 일부로 제공되는 모든 기존 컨트롤이 이 작업을 수행합니다. 스타일, 테마 및 컨트롤 디자인에 대한 자세한 내용은 스타일을 지정할 수 있는 컨트롤을 디자인하기 위한 지침을 참조하십시오.

DataTemplate에도 암시적 키가 있습니다. DataTemplate에 대한 암시적 키는 DataType 속성 값입니다. 또한 DataType{x:Type...}을 사용하여 명시적으로 지정되지 않고 형식 이름으로 지정될 수 있습니다. 자세한 내용은 데이터 템플릿 개요를 참조하십시오.

참고 항목

작업

방법: 리소스 정의 및 참조

개념

성능 최적화: 응용 프로그램 리소스

리소스 및 코드

응용 프로그램 관리 개요

참조

x:Type 태그 확장

ResourceDictionary

StaticResource 태그 확장

DynamicResource 태그 확장