1. 리소스의 개념과 구조
WPF(Windows Presentation Foundation)에서 리소스는 재사용 가능한 객체를 저장하는 메커니즘입니다. 모든 프레임워크 기반 요소는 Resources 속성을 가지고 있으며, 이 속성은 ResourceDictionary 타입입니다. 리소스를 조회할 때는 현재 컨트롤에서 시작하여 부모 요소를 거치ながら Application 수준까지 순차적으로 검색됩니다.
1.1 윈도우와 컨트롤 리소스 정의
리소스는 люб élément에서 정의할 수 있지만, 通常은 루트 요소인 Window나 Page에서 정의합니다.
<Window.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="Navy"/>
</Window.Resources>
<DockPanel>
<!-- 정적 리소스: 로드 시 한 번만 참조 -->
<Ellipse Fill="{StaticResource PrimaryBrush}" Width="120" Height="120"/>
<!-- 동적 리소스: 런타임에 다시 조회 -->
<Ellipse Fill="{DynamicResource PrimaryBrush}" Width="120" Height="120"/>
<!-- 로컬 리소스가 있는 컨트롤 -->
<Ellipse x:Name="Shape1" Width="120" Height="120" Fill="{DynamicResource PrimaryBrush}">
<Ellipse.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="Crimson"/>
</Ellipse.Resources>
</Ellipse>
</DockPanel>
1.2 애플리케이션 전역 리소스
특정 윈도우나 페이지에서 리소스를 찾지 못하면, WPF는 애플리케이션 수준에서 리소스를 검색합니다. Visual Studio에서는 App.xaml 파일의 Application.Resources 섹션에 정의합니다.
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
StartupUri="MainWindow.xaml">
<Application.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="Navy"/>
</Application.Resources>
</Application>
1.3 시스템 리소스 참조
애플리케이션 리소스에서도 원하는 리소스를 찾지 못하면, WPF는 시스템 리소스를 추가로 검색합니다. 이러한 시스템 리소스는 다음 네임스페이스에서 제공됩니다:
- SystemColors: 운영체제의 색상 설정에 접근
- SystemFonts: 시스템 폰트 구성 정보 제공
- SystemParameters: 화면 해상도, 마우스/키보드 설정, 그래픽 효과 등 다양한 시스템 매개변수 포함
2. 정적 리소스와 동적 리소스의 차이점
정적 리소스(StaticResource)는 애플리케이션이 메모리에 로드될 때 한 번만 리소스를 확인하고 그 이후에는 다시 참조하지 않습니다. 반면에 동적 리소스(DynamicResource)는 애플리케이션 실행 중에도 리소스 조회르 계속 수행합니다.
런타임 중 리소스 변경이 필요한 경우를 살펴보겠습니다:
public MainWindow()
{
InitializeComponent();
this.Resources["PrimaryBrush"] = new SolidColorBrush(Colors.Teal);
}
위의 코드를 실행하면 정적 리소스로 참조한 요소는 변경 사항이 반영되지 않고, 동적 리소스로 참조한 요소만 새로운 색상으로 업데이트됩니다.
리소스 조회 메서드:
FindResource() 메서드 대신 TryFindResource()를 사용하면 리소스를 찾지 못할 때 예외 대신 null을 반환하여 더 안전합니다.
var brush = (SolidColorBrush)Shape1.TryFindResource("PrimaryBrush");
3. 리소스 딕셔너리 활용
리소스 딕셔너리는 순수 XAML 문서로, 재사용 가능한 리소스를 저장하는 용도로만 사용됩니다. ResourceDictionary의 Source 속성에 파일 경로를 지정하여 외부 리소스 딕셔너리를 로드할 수 있습니다.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="PrimaryBrush" Color="Navy"/>
</ResourceDictionary>
애플리케이션 전역에서 여러 리소스 딕셔너리를 공유하려면 App.xaml의 병합된 딕셔너리에 포함시킵니다:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ThemeDictionary.xaml"/>
<ResourceDictionary Source="ColorPalette.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
개별 윈도우에서만 사용하려면 Window.Resources에 직접 지정합니다:
<Window.Resources>
<ResourceDictionary Source="LocalResources.xaml"/>
</Window.Resources>
4. 리소스 딕셔너리 병합과 컴포넌트 공유
4.1 리소스 딕셔너리 생성
여러 프로젝트에서 공통으로 사용할 리소스를 정의하려면 별도의 WPF 라이브러리 프로젝트를 생성합니다. 리소스 딕셔너리 파일(SharedResources.xaml)을 만들고 ComponentResourceKey를 사용하여 리소스를 정의합니다:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControls">
<SolidColorBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:BaseControl},ResourceId=ThemeBrush}"
Color="Navy"/>
</ResourceDictionary>
이미지나 바이너리 리소스를 포함할 때는 절대 Pack URI 경로를 사용하고, 빌드 작업을 "Resource"로 설정해야 합니다.
Pack URI 구문:
pack://application,,,[/어셈블리명;][버전정보;][폴더명/]파일명
Pack URI 사용 시 주의사항:
- 경로는 우에서 좌로 해석되는 슬래시(/) 사용
- 축약형은 상대 경로로, UriKind는 Relative
- 전체 형식은 절대 경로로, UriKind는 Absolute
- 상대 경로에서 ./는 현재 디렉토리, ../는 상위 디렉토리 의미
4.2 리소스 구성
여러 리소스 딕셔너리를 통합하려면 Generic.xaml 파일에 병합 구성을 추가합니다:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SharedLibrary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/SharedLibrary;component/SharedResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
동일한 결과를 주는 C# 코드:
Uri relativeUri = new Uri(@"./SharedResources.xaml", UriKind.Relative);
Uri absoluteUri = new Uri(@"pack://application:,,,/SharedLibrary;component/SharedResources.xaml", UriKind.Absolute);
커스텀 컨트롤 클래스 정의:
public class BaseControl : Control
{
public static ComponentResourceKey ThemeBrush
{
get
{
return new ComponentResourceKey(typeof(BaseControl), "ThemeBrush");
}
}
}
4.3 컴포넌트 빌드 및 사용
SharedLibrary.dll을 빌드한 후 새 프로젝트에서 참조합니다. XAML 네임스페이스를 선언하고 리소스를 사용합니다:
<Window x:Class="AppUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AppUI"
xmlns:controls="clr-namespace:SharedLibrary;assembly=SharedLibrary"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="900">
<DockPanel>
<!-- ComponentResourceKey는 반드시 DynamicResource로 참조 -->
<Ellipse Fill="{DynamicResource {x:Static controls:BaseControl.ThemeBrush}}" Width="150" Height="150"/>
</DockPanel>
</Window>