스타일·리소스·테마 관리
스타일·리소스·테마 관리
요약: ResourceDictionary에 색과 스타일을 모아 StaticResource/DynamicResource로 참조하고, 파일 분리로 테마를 운영하는 방법을 정리한다.
1. 왜 리소스인가
창마다 Background="#0B6E4F"를 복사하면 브랜드 색을 바꿀 때 누락이 생긴다. WPF는 키로 찾는 리소스 사전에 브러시·스타일·템플릿을 둔다. 화면은 키만 참조한다.
언제 사전을 나누는가.
| 상황 | 위치 |
|---|---|
| 이 버튼만 | 컨트롤 Resources |
| 이 창만 | Window.Resources |
| 앱 전역 색·기본 버튼 | App.xaml 또는 병합 사전 |
| 라이트/다크 전환 | 테마별 ResourceDictionary + DynamicResource |
2. 핵심 개념
| 개념 | 한 줄 |
|---|---|
x:Key |
사전에서 리소스를 찾는 이름 |
StaticResource |
로드 시 한 번 해석. 일반 색·스타일 |
DynamicResource |
런타임에 키가 바뀌면 다시 적용. 테마 전환 |
Style |
같은 타입에 Setter로 속성을 묶는다 |
TargetType |
키 없이 그 타입 전체에 암시 스타일 |
MergedDictionaries |
여러 .xaml 사전을 한곳으로 합친다 |
StaticResource가 더 싸다. 실행 중 색을 갈아끼울 때만 DynamicResource를 쓴다. 없는 키는 로드 시 예외(Static) 또는 빈 값(Dynamic)으로 나타난다.
3. 동작하는 예: 색과 버튼 스타일
Resources/Colors.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryColor">#0B6E4F</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="SurfaceBrush" Color="#F4F7F6" />
<SolidColorBrush x:Key="TextBrush" Color="#1A1A1A" />
</ResourceDictionary>
Resources/Controls.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Padding" Value="12,6" />
<Setter Property="Margin" Value="4,0,0,0" />
<Setter Property="Background" Value="{StaticResource PrimaryBrush}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style x:Key="GhostButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{StaticResource PrimaryBrush}" />
</Style>
</ResourceDictionary>
BasedOn으로 기본 버튼에서 일부만 덮는다. GhostButton은 키가 있으므로 명시적으로 적용한다.
App.xaml:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Colors.xaml" />
<ResourceDictionary Source="Resources/Controls.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
병합 순서가 중요하다. Controls.xaml이 PrimaryBrush를 쓰므로 색 사전을 먼저 넣는다.
창에서:
<Grid Background="{StaticResource SurfaceBrush}">
<StackPanel Margin="16">
<TextBlock Text="제목" Foreground="{StaticResource TextBrush}" />
<Button Content="저장" />
<Button Content="취소" Style="{StaticResource GhostButton}" />
</StackPanel>
</Grid>
첫 Button은 TargetType="Button" 암시 스타일이 적용된다. 둘째는 GhostButton 키를 지정한다.
4. 테마 전환
다크 사전 Resources/Colors.Dark.xaml에 같은 키 이름으로 다른 Color를 둔다. 런타임에 MergedDictionaries의 해당 항목을 교체한다.
var appDict = Application.Current.Resources.MergedDictionaries;
var old = appDict.First(d => d.Source?.OriginalString.Contains("Colors") == true);
appDict.Remove(old);
appDict.Insert(0, new ResourceDictionary
{
Source = new Uri("Resources/Colors.Dark.xaml", UriKind.Relative),
});
브러시 참조는 DynamicResource여야 교체가 화면에 반영된다.
<TextBlock Foreground="{DynamicResource TextBrush}" />
키 이름을 테마마다 다르게 만들면 화면 XAML을 다 고쳐야 한다. 같은 키, 다른 값이 테마의 규칙이다.
5. 주의 / 흔한 실수
- 병합 순서 반대로. 스타일이 색보다 먼저면
StaticResource PrimaryBrush를 못 찾는다. - 암시 스타일(
TargetType만)을 서드파티 컨트롤에 광역 적용. 라이브러리 버튼까지 브랜드 색이 덮인다. 앱 컨트롤만 키 스타일로 제한하는 편이 안전하다. - 테마는
StaticResource로 색을 고정. 사전을 갈아도 이미 해석된 값이 남는다. 전환 대상은DynamicResource. Style안에서 같은 사전의 키를DynamicResource로 순환 참조. 로드가 실패한다.- 창
Resources에 앱과 같은 키를 다시 정의. 가까운 사전이 이긴다. 의도가 아니면 중복 키를 만들지 않는다.
정리
WPF UI 일관성은 창을 하나씩 고치는 일이 아니라 리소스 사전을 시스템으로 두는 일이다.
- 색·간격 →
Colors.xaml키 - 컨트롤 기본 모습 →
Style+MergedDictionaries - 테마 → 같은 키 +
DynamicResource+ 사전 교체
연습
PrimaryBrush색을 바꾸고 저장·암시 스타일 버튼이 같이 바뀌는지 확인한다.GhostButton만 테두리를 추가해 기본 버튼과 구분되게 한다.Colors.Dark.xaml을 만들고TextBrush를 밝은 색으로 둔 뒤, 버튼을 눌러 사전을 교체해 본다 (DynamicResource필요).
댓글
0아직 댓글이 없습니다. 첫 댓글을 남겨보세요.