This article has been localized into Korean by the community.
리소스
WPF는 매우 편리한 개념을 도입했습니다 : 데이터를 리소스 형태로 저장하는 것입니다. 컨트롤이나 윈도우를 지역적으로 저장하거나, 전체 어플리케이션을 전역으로 저장할 수 있습니다. 이때 데이터는 당신이 원하는 모든 데이터입니다. 실제 정보일수도 있고, WPF 컨트롤들의 계층구조일 수도 있습니다. 이 개념은 데이터를 한 곳 또는 몇몇 장소에 저장해서 사용할 수 있어 매우 유용합니다.
이 개념은 스타일과 템플릿에서 자주 사용합니다. 이번 챕터에서는 간략히만 알아보고 나중에 다시 다루겠습니다. 당신은 이 개념을 매우 다양한 곳에서 활용할 수 있습니다. 다음의 간단한 예제에서 증명해 보겠습니다 :
<Window x:Class="WpfTutorialSamples.WPF_Application.ResourceSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ResourceSample" Height="150" Width="350">
<Window.Resources>
<sys:String x:Key="strHelloWorld">Hello, world!</sys:String>
</Window.Resources>
<StackPanel Margin="10">
<TextBlock Text="{StaticResource strHelloWorld}" FontSize="56" />
<TextBlock>Just another "<TextBlock Text="{StaticResource strHelloWorld}" />" example, but with resources!</TextBlock>
</StackPanel>
</Window>
리소스는 x:Key 속성으로 키를 받습니다. 키를 이용해 어플리케이션 내 다른 위치에 있는 값을 참조할 수 있습니다. 키는 markup extension인 StaticResource와 결합되어 있습니다. 이 예제에서는 간단한 문자열을 저장하였고 두 개의 다른 TextBlock 컨트롤에서 사용했습니다.
StaticResource와 DynamicResource
지금까지의 예제에서는 리소스를 참조하기 위해 StaticResource markup extension을 사용했습니다. 하지만 DynamicResource라는 다른 형태의 대안이 있습니다.
정적 리소스의 경우, XAML이 로드될 때 단 한번 할당된다는 것이 주요 특징입니다. StaticResource를 사용하면 나중에 리소스가 변경되더라도 변경사항이 반영되지 않습니다.
반면에 DynamicResource는 실제 필요할 때마다 할당합니다. 리소스가 변경될 때 마다 반복해서 할당할 수 있습니다. 정적인 값을 바인딩하는 경우와, 이 값을 모니터링하여 변경 시 값을 전달하는 기능을 바인딩하는 경우를 생각해보겠습니다. 정답이 될 수는 없지만 어떤 방법을 사용하는 것이 나을 지에 대하여 좋은 아이디어를 줄 수 있습니다. 동적인 리소스는 디자인 타임에는 존재하지 않았던 리소스들도 사용할 수 있습니다. 예를 들어 어플리케이션을 시작할 때 Code-behind에서 추가할 수 있습니다.
다양한 리소스 타입
간단한 문자열을 사용하는 것은 쉬운 예지만, 그 외에 많은 것들도 할 수 있습니다. 다음의 예제에서는 문자열로 이루어진 배열과 배경으로 쓰일 gradient brush를 추가해보겠습니다. 리소스를 가지고 얼마나 많은 일들을 할 수 있는지에 대해 좋은 아이디어를 줄 것입니다.
<Window x:Class="WpfTutorialSamples.WPF_Application.ExtendedResourceSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ExtendedResourceSample" Height="160" Width="300"
Background="{DynamicResource WindowBackgroundBrush}">
<Window.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
<x:Array x:Key="ComboBoxItems" Type="sys:String">
<sys:String>Item #1</sys:String>
<sys:String>Item #2</sys:String>
<sys:String>Item #3</sys:String>
</x:Array>
<LinearGradientBrush x:Key="WindowBackgroundBrush">
<GradientStop Offset="0" Color="Silver"/>
<GradientStop Offset="1" Color="Gray"/>
</LinearGradientBrush>
</Window.Resources>
<StackPanel Margin="10">
<Label Content="{StaticResource ComboBoxTitle}" />
<ComboBox ItemsSource="{StaticResource ComboBoxItems}" />
</StackPanel>
</Window>
이번에는 몇 가지 다른 리소스를 추가했습니다. 윈도우는 간단한 문자열, 문자열 배열, LinearGradientBrush를 포함하고 있습니다. 문자열은 라벨에서 이용하고 문자열 배열은 콤보박스 컨트롤에서 이용합니다. gradient brush는 전체 윈도우 배경에 이용합니다. 위에서 볼 수 있듯이 리소스로 어떤 것이든 저장할 수 있습니다.
로컬 리소스와 어플리케이션 리소스
지금까지는 윈도우 레벨에서 리소스를 저장했습니다. 즉 윈도우의 모든 부분에서 리소스에 접근할 수 있었습니다.
하나의 특정 컨트롤을 위한 리소스가 필요할 경우, 윈도우가 아닌 컨트롤에 추가해서 지역적으로 사용할 수 있습니다. 작동 방식은 동일합니다. 유일한 차이점은 생성된 컨트롤 범위 내에서만 접근할 수 있다는 것입니다 :
<StackPanel Margin="10">
<StackPanel.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</StackPanel.Resources>
<Label Content="{StaticResource ComboBoxTitle}" />
</StackPanel>
이 경우에는 StackPanel에 리소스를 추가하고 하위 컨트롤인 Label에서 추가한 리소스를 사용했습니다. StackPanel 내 다른 컨트롤도 마찬가지로 이 리소스를 사용할 수 있습니다. 하위 컨트롤의 하위 컨트롤도 사용할 수 있습니다. 그러나 StackPanel 외부에서는 접근할 수 없습니다.
여럿의 윈도우에서 리소스에 접근해야 하는 경우도 가능합니다. 윈도우나 WPF 컨트롤이 리소스를 포함할 수 있는 것처럼 App.xaml 파일도 리소스를 포함할 수 있습니다. App.xaml 파일에 리소스를 저장하면, 전역적으로 프로젝트 내 모든 윈도우와 컨트롤에서 접근할 수 있습니다. 윈도우에 리소스를 저장하고 사용하는 방식과 동일하게 작동합니다 :
<Application x:Class="WpfTutorialSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="WPF application/ExtendedResourceSample.xaml">
<Application.Resources>
<sys:String x:Key="ComboBoxTitle">Items:</sys:String>
</Application.Resources>
</Application>
사용 방법도 동일합니다 - WPF는 주어진 리소스를 찾기 위해 자동으로 로컬 컨트롤에서 윈도우로, App.xaml로 범위를 올려갑니다.
<Label Content="{StaticResource ComboBoxTitle}" />
Code-behind의 리소스
지금까지는 markup extension을 이용해서 XAML에서 바로 리소스에 접근했습니다. 하지만 Code-behind에서도 리소스에 접근할 수 있으며 몇몇 경우에는 더 유용할 수 있습니다. 이전 예제에서 어떻게 리소스를 각기 다른 곳에 저장하는지 알아보았습니다. 다음 예제에서는 Code-behind 각기 다른 곳에 저장된 리소스에 접근해보도록 하겠습니다.
App.xaml:
<Application x:Class="WpfTutorialSamples.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="WPF application/ResourcesFromCodeBehindSample.xaml">
<Application.Resources>
<sys:String x:Key="strApp">Hello, Application world!</sys:String>
</Application.Resources>
</Application>
Window:
<Window x:Class="WpfTutorialSamples.WPF_Application.ResourcesFromCodeBehindSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="ResourcesFromCodeBehindSample" Height="175" Width="250">
<Window.Resources>
<sys:String x:Key="strWindow">Hello, Window world!</sys:String>
</Window.Resources>
<DockPanel Margin="10" Name="pnlMain">
<DockPanel.Resources>
<sys:String x:Key="strPanel">Hello, Panel world!</sys:String>
</DockPanel.Resources>
<WrapPanel DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10">
<Button Name="btnClickMe" Click="btnClickMe_Click">Click me!</Button>
</WrapPanel>
<ListBox Name="lbResult" />
</DockPanel>
</Window>
Code-behind:
using System;
using System.Windows;
namespace WpfTutorialSamples.WPF_Application
{
public partial class ResourcesFromCodeBehindSample : Window
{
public ResourcesFromCodeBehindSample()
{
InitializeComponent();
}
private void btnClickMe_Click(object sender, RoutedEventArgs e)
{
lbResult.Items.Add(pnlMain.FindResource("strPanel").ToString());
lbResult.Items.Add(this.FindResource("strWindow").ToString());
lbResult.Items.Add(Application.Current.FindResource("strApp").ToString());
}
}
}
위와 같이 우리는 3개의 다른 "Hello, world!" 메시지를 저장했습니다 : App.xaml에 하나, window 내부에 하나, 지역적으로 main panel에 하나 입니다. 인터페이스는 버튼 하나와 리스트박스 하나로 구성되어 있습니다.
Code-behind에서는 버튼의 클릭 이벤트를 다룹니다. 스크린샷에서 볼 수 있듯이, 각각의 문자열 텍스트를 추가합니다. FindResource()라는 메소드를 사용해서 리소스를 (찾으면) object로 리턴합니다. 그리고 ToString() 메소드를 사용해 우리가 알고있는 string으로 변환합니다.
각기 다른 계층에서 어떻게 FindResource() 메소드를 사용하는지 주목해봅시다 - 첫 번째는 panel, 두 번째는 window, 마지막은 current Application object 입니다. 이미 알고 있는 곳에서 리소스를 찾는 것이 일반적이지만, 이전에 언급했던 대로 리소스를 찾지 못하면 계층 구조를 따라 검색합니다. 기본적으로 세 리소스 모두 Panel 계층에서 FindResource() 메소드를 사용해 찾을 수 있습니다. panel 레벨에서 찾지 못할 경우 window, application 레벨까지 탐색을 이어가기 때문입니다.
다른 방법으로는 같은 결과를 얻을 수 없습니다 - 검색은 트리구조의 아래 방향으로 진행하지 않습니다. 즉 리소스가 컨트롤에 지역적으로, 또는 윈도우에 정의되어 있으면 application 레벨부터 검색해서는 찾을 수 없습니다.