본문으로 바로가기

WPF Resource

category Development/C# 2012. 3. 15. 11:43

WPF 리소스는 객체와 값을 재사용하기 위해 제공하는 방법이다. 리소스는 XAML과 코드 모두를 이용해 다룰 수 있다.
프레임 수준 객체에서 리소tm는
관리되는데 WPF에서는 Core 부분과 Framework 부분으로 기능을 크게 나누어 분리하는데 리소스는 Framework 부분에서 다루어진다. FrameworkElement 또는 FrameworkContentElement와 여기에서 파생되는 객체는 Resources라는 속성을 가지는데 ResourceDictionary라는 Type을 가진다. 각 객체가 자체 저장소를 가지고 스스로의 자료를 관리 한다. 객체는 데이터의 저장소이자 소유자이며 사용 영역인 셈이다. 트리의 하부 요소는 상부 요소의 리소스에 접근 할 수 있다.

<Window x:Class="WpfApplication54.Window1"
 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 
Title="Window1" Height="177" Width="300">

<Window.Resources>
   <SolidColorBrush x:Key="MyBrush" Color="Aqua"/>
</Window.Resources>

<StackPanel Height="108">
   <Button Height="23" Name="button1" VerticalAlignment="Top"
       
Background="{StaticResource MyBrush}">Button</Button>
</StackPanel>

</Window>

Window 객체도 FrameworkElement이고 코드에서 Resources를 가지고 있다.
Window 리소스에서 MyBrush 라는 값을 하나 정의하고 있다. Window 내에서 언제든지 사용 할 수 있게 되었다.
XAML 리소스에 정의 된 요소들은
어플리케이션이 초기화 될 때 인스턴스화 되고 관리된다.
Button은 StatckPanel에
속해 있지만 Windows에 속해 있다.

Button의 Background 속성은 XAML 태그 확장이라는 기능을 통해 Window 리소스를 참조하고 있다.
다음과 같이 조금 리소스 위치를 옮겨도 Button 에는 동일한 효과가 날 것이다.

<StackPanel Height="108">
  
<StackPanel.Resources>
     
<SolidColorBrush x:Key="MyBrush" Color="Aqua"/>
  
</StackPanel.Resources>
   <Button Height="23" Name="button1" VerticalAlignment="Top"
       
Background="{StaticResource MyBrush}">Button</Button>
</StackPanel>

Dynamic Resource 와 Static Resource

동적 정적 리소스로 번역이 되는 데 정적 리소스는 처음 참조가 한번 이루어지고 리소스의 변경을 관리 하지 않는다. 하지만 동적 리소스는 값이 사용 될 때 마다 변경 여부가 확인이 이루어진다. 즉 실시간 계산이 보류 되었다가 계산이 된다.
동적인 혹은 정적인 리소스를 사용 할 지를 결정하는 기준으로는 데이터의 변경 추적을 얼마나 자주 해야 하느냐의 문제이므로 데이터 바인딩과 변경이 동시에 이루어지는 시나리오에서는 동적 리소스를 하지만 변경이 거의 일어나지 않는 시나리오에서는 정적 리소스를 참조를 하는 것이 마땅하다.

참조를 위해서 XAML에서는 태그 확장이라는 문법을 사용한다.

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

태그 확장이라는 문법을 사용 할 때 코드의 형태는 { } 즉 brace 사용하는 것이다. StaticResource라는 예약어를 사용하고 MyBrush 라는 킷값을 지정한다. 위의 코드는 리소스를 정적인 형태로 참조 한다는 것이다.

동적인 형태
로 참조를 할 때는 다음과 같이 하면 될 것이다

<Button Background="{DynamicResource MyBrush}"/>

정적인 리소스를 사용하는 것이 바람직한 경우는 다음과 같다.
처음으로 참조한 리소스의 값을 변경할 의도가 없는 경우
페이지나 응용프로그램에 리소스를 모아 주로 사용 할 경우
DependencyObject 또는 Freezable에 해당하지 않는 속성의 값을 설정하는 경우
DLL로 컴파일되고 응용 프로그램 일부로 패키징되거나 응용 프로그램간에 공유되는 리소스 사전을 만드는 경우
사용자 지정 컨트롤에 대한 테마를 만들고 해당 테마 내에 사용되는 리소스를 정의하는 경우

동적인 리소스를 사용하는 것이 바람직한 경우는 다음과 같다
런타임시에 값을 알 수 있는 경우
사용자 지정 컨트롤에 대한 테마 스타일을 만들거나 참조하는 경우
응용 프로그램 수명 동안 ResourceDictionary의 내용을 조정하려는 경우
참조된 리소스가 즉시 사용 되지 않을 경우
setter 값을 테마 또는 다른 사용자 설정의 영향을 받는 다른 값에서 가져 올 수 있는 스타일을 만드는 경우

Style 과 암시적인 키

리소스는 참조될 때 key 값을 기반으로 참조 된다. 이야기를 다르게 하면 모든 리소스들은 key 값을 가져야 한다는 것이다.
하지만 그렇지 않은 경우도 있다.
암시적인 key 값을 갖는 경우가 그러하다. 암시적이라는 말은 key라고 직접 분명하게 말하지 않는 다는 것이다. 하지만 내부적으로 가진다는 것이다.

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

위의 Style 코드는 x:key 값을 포함하지 않는다. 대신 TargetType을 직접 지정하고 있다.
이 부분이 x:key 부분을 대신하고 암시적인 key 값을 발생한다

TargetType="{x:Type Button}" 이렇게 사용해도 효과는 같다..

<StackPanel Height="108">
  
<Button>Hello</Button>
</StackPanel>

Button 에는 리소스 참조가 명시적으로 보이지 않는다. 하지만 WPF 프레임워크는 영역 안의 리소스들을 찾아 적용하게 되는데 그 중 하나가 자신의 Type에 적용된 리소스들을 찾는 것이다. Button은 TyrgetType 이 Button 으로 지정 된 값들을 찾아 적용하게 된다. 암시적으로 적용 되는 시스템 테마들은 이러한 메카니즘을 따른다. 하지만 TargetType보다 x:key 값이 우선한다 .TargetType으로 적용된 값이 x:key 값으로 오버라이딩 된다.



코드에서 리소스를 찾아 적용 할 때는 다음과 FrameworkElement 객체가 제공하는 FindResource 메소드를 이용하면 된다. 당연히 FrameworkElement 수준에서 리소스가 지원되기 시작하므로 리소스 검색과 접근을 위한 메소드 노출도FrameworkElement 수준에서 이루어 져야 한다.

void SetBGByResource(object sender, RoutedEventArgs e)
{
   Button b = sender as Button;
   b.Background = (Brush)this.FindResource("RainbowBrush");
}

리소스는 병합해 사용 할 수 있다. 여러 개의 파일로 나누어 관리 할 수 있다.
여러 개를 합쳐 하나의 리소스로 사용 할 수 가 있다.

<Page.Resources>
   <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
         <ResourceDictionary Source="myresourcedictionary.xaml"/>
         <ResourceDictionary Source="myresourcedictionary2.xaml"/>
      </ResourceDictionary.MergedDictionaries>
   </ResourceDictionary>
</Page.Resources>

시스템 리소스 사용

우리가 사용하는 리소스 중에 직접 정의하지 않았지만 시스템이 정의한 리소스들이 있다.
여기에 접근하기 위해서는 앞에 언급한 방법은 적절 하지가 못할 것이다.

WPF는 이러한 시스템 값들을 클래스로 정의를 해주고 멤버들을 정적 멤버로 선언해 지원한다.
응용프로그램 인스턴스가 아닌 것이다.

<Button Margin="10, 10, 5, 5" Grid.Column="0" Grid.Row="3" FontSize="{x:Static SystemFonts.IconFontSize}" FontWeight="{x:Static SystemFonts.MessageFontWeight}" FontFamily="{x:Static SystemFonts.CaptionFontFamily}">SystemFonts</Button>

XAML코드는 태그 확장 코드에서 x:Static 예약어를 사용 하고 있다. 참조하는 객체가 정적 객체임을 암시한다.
다음은 SystemFonts class의 MSDN
문서의 일부분이다. 대부분의 멤버가 정적인 값을 유지하고 있다.

WPF는 시스템의 값들df 정적 객체로 정의하고 참조 할수 있도록 하고 있음을 알 수 있다

다음은 시스템 값들을 모아 하나의 Style로 정의하고 있다.

한가지 주의 해야 할 점은 동적 리소스로 참조 할 때 IconFontSizeKey처럼 끝에 key라는 문자열이 추가 된다는 것이다.

<Style x:Key="SimpleFont" TargetType="{x:Type Button}">
   <Setter Property = "FontSize" Value= "{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}"/>
   <Setter Property = "FontWeight" Value= "{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}"/>
   <Setter Property = "FontFamily" Value= "{DynamicResource {x:Static SystemFonts.CaptionFontFamilyKey}}"/>
</Style>

시스템 값 중에 정해진 값이 상황에 따라 자주 변하고 시스템의 값을 파라메타로 정의해야 하는 경우가 있는데 이를 경우에 SystemParameters 객체를 이용해 접근한다.

<Button Height="{x:Static SystemParameters.CaptionHeight}" Width="{x:Static SystemParameters.IconGridWidth}">
Thank Parameter </Button>

위의 코드는 Button의 높이를 Caiption 의 높이값을 파라메타로 넘겨주고 있다.

WPF는 응용프로그램에서 사용하는 리소스와 시스템이 제공한 리소스로 구분하고 시스템이 제공하는 리소스는 WPF에서 정적인 값으로 미리 정의를 하고 있다.또한 재사용성의 효율성을 높이 위해 하나의 인스턴스를 공유 하는 형태를 취하고 있다