728x90
반응형

1. Logical & Visual Trees
* Logical Tree
- XAML에서 작성한 UI 요소의 논리적 관계 구조를 나타낸다.
- “어떤 요소가 어떤 요소의 자식인지”와 같은 구성 및 계층 관계를 반영한다.
- 데이터 바인딩, 리소스 탐색, 이벤트 라우팅(Logical Routing) 등에 사용된다.
- 특징
- 컨트롤의 구성 요소만 표시되며, 내부적으로 렌더링되는 세부 그래픽 요소(버튼의 사각형, 테두리 등)는 표시되지 않음
- FrameworkElement.Parent나 LogicalTreeHelper로 탐색 가능.
- 리소스(Resource) 탐색 경로는 Logical Tree를 따라 올라간다.
//xaml
<Window>
<StackPanel>
<Button Content="OK" />
<Button Content="Cancel" />
</StackPanel>
</Window>
// Logical Tree
Window
└─ StackPanel
├─ Button ("OK")
└─ Button ("Cancel")
* Visual Tree
- 실제 렌더링에 사용되는 모든 시각적 요소들의 계층 구조를 나타낸다
- Logical Tree의 각 요소가 렌더링되기 위해 더 세분화된 요소(Visual, Drawing, Border 등) 로 확장됨
- 특징
- 컨트롤 템플릿 내부의 구조, 각종 장식 요소(Decorators), 레이아웃 컨테이너 등이 모두 포함된다.
- 렌더링, Hit Testing(마우스 충돌 판정) 등에 사용된다.
- VisualTreeHelper를 사용해 탐색 가능
Button
└─ Border
└─ ContentPresenter
└─ TextBlock ("OK")
* Logical & Visual Tree 보기 예시
// Logical Tree 보기
foreach (var child in LogicalTreeHelper.GetChildren(myWindow))
{
Console.WriteLine(child);
}
// Visual Tree 보기
void PrintVisualTree(DependencyObject obj, int indent = 0)
{
Console.WriteLine(new string(' ', indent) + obj.GetType().Name);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
PrintVisualTree(VisualTreeHelper.GetChild(obj, i), indent + 2);
}
}
PrintVisualTree(myButton);
2. Control Template
- 기존 윈도우 컨트롤은 외양이 고정되어 있으며, WPF 이전에는 커스터마이징이 제한적이었음.
- WPF의 ControlTemplate은 이러한 제약을 깨고, 컨트롤의 Visual Tree를 완전히 새롭게 구성할 수 있는 자유를 제공
* 구조와 정의
- ControlTemplate은 XAML 리소스로 정의되며, Control.Template 속성에 할당해서 컨트롤의 외관을 바꿀 수 있음.
- 주로 스타일(Style)에 포함시켜 재사용하거나 전역 리소스로 공유 가능
* Trigger와 Visual States
- ControlTemplate 내부에서는 Trigger, EventTrigger, DataTrigger 등을 활용해 상태에 따른 시각 효과나 애니메이션을 정의할 수 있음.
- WPF의 VisualStateManager를 이용하면, 상태 기반(예: Normal, MouseOver 등) 시각 효과 전환도 가능
* 예제들
- 버튼 외형 변경
- ContentPresenter가 원래 버튼의 Content를 표시.
- Logical Tree에는 여전히 Button이지만, Visual Tree는 Border와 ContentPresenter로 구성.
<Window.Resources>
<ControlTemplate x:Key="FlatButtonTemplate" TargetType="Button">
<Border Background="LightBlue" CornerRadius="5" Padding="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Window.Resources>
<StackPanel Margin="20">
<Button Template="{StaticResource FlatButtonTemplate}" Content="Click Me!" Width="100" Height="40"/>
</StackPanel>
- 스타일과 결합해서 전역 적용
- Template을 스타일에 넣으면 해당 윈도우의 모든 Button에 적용.
- 코드 중복 없이 외형 통일 가능.
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Orange" CornerRadius="3" Padding="8">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Content="OK"/>
<Button Content="Cancel"/>
</StackPanel>
- Trigger로 상태 변화
- Trigger를 이용해 마우스 오버, 클릭 상태에서 배경색 변경.
- VisualStateManager를 쓰면 더 정교하게 가능
<Window.Resources>
<ControlTemplate x:Key="HoverButtonTemplate" TargetType="Button">
<Border x:Name="border" Background="LightGray" CornerRadius="5" Padding="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="SkyBlue"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="SteelBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Button Template="{StaticResource HoverButtonTemplate}" Content="Hover Me!" Width="120" Height="40"/>
- checkbox의 커스터마이징
- 체크박스의 체크 마크 대신 배경색으로 상태 표현.
- 기존 테마의 체크박스 모양 완전 제거 가능.
<Window.Resources>
<ControlTemplate x:Key="CustomCheckBoxTemplate" TargetType="CheckBox">
<StackPanel Orientation="Horizontal">
<Border x:Name="box" Width="20" Height="20" BorderBrush="Black" BorderThickness="1" Margin="0,0,5,0"/>
<ContentPresenter VerticalAlignment="Center"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="box" Property="Background" Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<CheckBox Template="{StaticResource CustomCheckBoxTemplate}" Content="Accept Terms"/>
- ItemsControl 커스터마이징
- ItemsPresenter는 ItemsControl의 아이템 목록을 표시하는 핵심 요소.
- 템플릿으로 감싸면 외형만 변경.
<Window.Resources>
<ControlTemplate x:Key="CustomItemsControlTemplate" TargetType="ItemsControl">
<Border BorderBrush="Black" BorderThickness="1" Padding="5">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Window.Resources>
<ItemsControl Template="{StaticResource CustomItemsControlTemplate}">
<sys:String>Item 1</sys:String>
<sys:String>Item 2</sys:String>
<sys:String>Item 3</sys:String>
</ItemsControl>
- Button + VisualStateManager 예제
- VisualStateManager로 상태 변화에 애니메이션 부여.
- 복잡한 UI 상태 전환에 유용
<Window.Resources>
<ControlTemplate x:Key="VSMButtonTemplate" TargetType="Button">
<Border x:Name="border" Background="Gray" CornerRadius="4" Padding="5">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="LightBlue" Duration="0:0:0.2"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="border"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="DarkBlue" Duration="0:0:0.1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ControlTemplate>
</Window.Resources>
<Button Template="{StaticResource VSMButtonTemplate}" Content="VSM Button"/>
* Templates vs Style
둘 다 UI 외형을 바꾸지만 적용 범위와 기능에서 차이가 있다.
| 구분 | Style | Template |
| 목적 | 컨트롤 속성 집합을 재사용 가능하게 묶어서 적용 | 컨트롤의 시각적 구조(Visual Tree) 자체를 변경 |
| 적용 대상 | 거의 모든 DependencyObject (주로 FrameworkElement) |
주로 Control 및 하위 클래스 |
| 변경 범위 | 색상, 크기, 폰트, 여백, 패딩 등 속성 값 변경 | 버튼의 테두리, 배경, 내부 구조 등 UI 구성 요소 자체 변경 |
| 핵심 요소 | Setter, Trigger, BasedOn | ControlTemplate, DataTemplate, ItemsPanelTemplate |
| 예시 효과 | 버튼 색을 빨간색으로, 글자 크기를 16으로 변경 | 버튼을 완전 평면 UI로, 또는 이미지를 배경으로 한 원형 버튼으로 변경 |