728x90
반응형

WPF에서 그래픽을 더 풍부하고 멋있게 만드는 이펙트 (Effects)와 하위 레벨 렌더링 API 비주얼(Visual)에 대해서
1. 이펙트 (Effects)
주로 Pixel Shader 기반의 이펙트를 사용해 UI 요소에 실시간 그래픽 효과를 줄 수 있는데 대표적인 내장 이펙트는 다음과 같다
- DropShadowEffect: 그림자 효과
- BlurEffect: 블러(흐림) 효과
- BitmapEffect (구버전, 권장 안 함)
- 사용법 예제 : DropShadowEffect
- Effect 속성에 이펙트를 지정해서 해당 요소 전체에 효과 적용
- GPU 가속 지원, 고성능 실시간 효과 가능
<Button Content="Shadow Button" Width="150" Height="40">
<Button.Effect>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="5" BlurRadius="10" Opacity="0.5"/>
</Button.Effect>
</Button>
* Custom Shader Effects
- WPF는 HLSL 기반의 픽셀 셰이더를 커스터마이징해서 직접 작성할 수 있음
- 컴파일된 .ps 파일을 프로젝트에 포함하고 ShaderEffect 클래스를 상속해 커스텀 효과 제작 가능
- 하지만 고급 주제이며, 책에서는 기초 및 예제를 소개함
2. 비주얼 (Visual)
WPF의 렌더링 하위 레벨 객체 모델.
Visual 클래스는 화면에 그려지는 모든 요소의 기본 클래스이며, 고성능 그래픽 작업, 직접 그리기, 커스텀 컨트롤 구현 시 주로 사용
- DrawingVisual: 벡터 그래픽, 텍스트, 이미지 등 빠른 그리기용
- ContainerVisual: 자식 Visual을 포함하는 그룹 역할
- HostVisual: 다른 스레드에서 렌더링 할 때 사용
- DrawingVisual 예제
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawRectangle(Brushes.LightBlue, new Pen(Brushes.Blue, 2), new Rect(10, 10, 100, 50));
dc.DrawText(
new FormattedText("Hello Visuals",
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Segoe UI"),
16, Brushes.Black),
new Point(15, 15));
}
* DrawingContext
WPF의 저수준 그래픽 렌더링 API에서 사용하는 핵심 클래스
DrawingVisual이나 OnRender 오버라이드 안에서 DrawingContext를 통해 직접 그리기 작업을 수행
- 역할: 선, 도형, 텍스트, 이미지 등을 화면에 그리는 메서드를 제공
- 사용처: DrawingVisual.RenderOpen(), OnRender(DrawingContext dc), Visual 하위 클래스 등에서 사용
- 생명주기: RenderOpen()으로 획득 → 그림 명령 실행 → Close()로 마무리
- 불변성: 그리기 후 DrawingContext는 닫히고 재사용 불가 → 매번 새로 열고 닫음
1) 주요 메서드
| 메서드 | 설명 | 주요 파라미터 |
| DrawLine | 직선 그리기 | Pen, 시작점(Point), 끝점(Point) |
| DrawRectangle | 사각형 채우기 및 테두리 그리기 | Brush, Pen, Rect |
| DrawEllipse | 원 또는 타원 그리기 | Brush, Pen, 중심점(Point), 반지름(x,y) |
| DrawGeometry | 복잡한 Geometry 그리기 | Brush, Pen, Geometry |
| DrawText | 텍스트 그리기 | FormattedText, 위치(Point) |
| DrawImage | 이미지 그리기 | ImageSource, Rect |
| PushClip | 클리핑 영역 지정 | Geometry |
| PushOpacity | 투명도 적용 | double (0~1) |
| PushTransform | 변환 행렬 적용 | Transform |
| Pop | 이전에 푸시한 상태 되돌리기 | 없음 |
2) DrawingContext 사용예시
protected override void OnRender(DrawingContext dc)
{
// 파란 사각형 그리기
dc.DrawRectangle(Brushes.LightBlue, new Pen(Brushes.Blue, 2), new Rect(10, 10, 100, 60));
// 빨간 원 그리기
dc.DrawEllipse(Brushes.Red, new Pen(Brushes.DarkRed, 1), new Point(200, 50), 40, 30);
// 직선 그리기
dc.DrawLine(new Pen(Brushes.Green, 3), new Point(10, 100), new Point(200, 100));
// 텍스트 그리기
var ft = new FormattedText(
"Hello DrawingContext",
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Segoe UI"),
16,
Brushes.Black,
VisualTreeHelper.GetDpi(this).PixelsPerDip);
dc.DrawText(ft, new Point(10, 120));
}
3) Push / Pop 상태 스택
DrawingContext는 그리기 상태를 스택 형태로 관리한다.
- Push* 메서드는 현재 상태를 변경하면서 이전 상태를 스택에 저장
- 반드시 Pop()으로 되돌려야 함 (스택 언더플로우 주의)
dc.PushOpacity(0.5);
dc.DrawRectangle(Brushes.Blue, null, new Rect(0, 0, 100, 100));
dc.Pop(); // 이전 상태 복원
4) 활용팁
- DrawingVisual과 함께 고성능 커스텀 그리기에 필수
- 복잡한 UI는 DrawingGroup과 함께 사용해 재활용 가능
- 성능 최적화를 위해 Freeze() 가능한 브러시, 펜, Geometry는 미리 얼려 두는 게 좋음
* Wrapping visuals in an Element
Visual 객체를 직접 관리하면서 UIElement처럼 동작하게 감싸는 (Wrapping) 기법
Visual 또는 DrawingVisual 같은 저수준 그래픽 객체를 컨트롤처럼 다루고 레이아웃, 입력 이벤트 처리 등 UI 요소 역할을 하도록 하는 패턴.
- Wrapping하는 이유
- Visual은 UIElement가 아니므로 기본적인 레이아웃, 입력, 포커스 처리 기능이 없음
- 따라서 Visual을 컨테이너 역할을 하는 UIElement 상속 클래스 내에서 자식으로 등록하고 관리해야 함
- 이를 통해 커스텀 컨트롤 개발 시 효율적이고 가벼운 렌더링 가능
- AddVisualChild 와 RemoveVisualChild
- UIElement 기반 클래스에 Visual 자식을 수동으로 등록/해제하는 메서드
- 등록된 Visual은 WPF 렌더링 트리에 포함되고, 렌더링과 입력 처리가 가능해짐
VisualChildrenCount 와 GetVisualChild 는 Visual 트리 탐색에 반드시 구현해야 하는 메서드 (VisualCollection 관리 시 필수)
protected override int VisualChildrenCount => _visuals.Count;
protected override Visual GetVisualChild(int index)
{
return _visuals[index];
}
예시) Visual래핑하는 커스텀 컨트롤
- isualCollection을 통해 Visual 자식을 관리
- VisualChildrenCount, GetVisualChild 오버라이드 필수
- 이제 이 VisualHost를 XAML이나 코드에서 UI 요소처럼 사용 가능
public class VisualHost : FrameworkElement
{
private readonly VisualCollection _children;
public VisualHost()
{
_children = new VisualCollection(this);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext dc = drawingVisual.RenderOpen())
{
dc.DrawEllipse(Brushes.LightBlue, new Pen(Brushes.DarkBlue, 2), new Point(50, 50), 40, 30);
}
_children.Add(drawingVisual);
}
protected override int VisualChildrenCount => _children.Count;
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count)
throw new ArgumentOutOfRangeException();
return _children[index];
}
}
* Visual Layer의 장점
- 높은 성능: Visual은 UIElement보다 가볍고 렌더링 속도가 빠름
- 직접 그리기 가능: 세밀한 그래픽 처리, 애니메이션 효과 구현에 적합
- 낮은 메모리 오버헤드
- 주의점
- Visual은 기본적으로 입력 이벤트 처리 기능 없음
- 직접 이벤트 처리 구현해야 함 (HitTest 등)
- 레이아웃 관리 기능 없음 (크기 조정, 정렬 따로 구현 필요)
using System.Windows;
using System.Windows.Media;
using System.Windows.Input;
public class VisualHost : FrameworkElement
{
private readonly VisualCollection _children;
public VisualHost()
{
_children = new VisualCollection(this);
// 새로운 DrawingVisual 생성
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
// 사각형 그리기
dc.DrawRectangle(Brushes.LightBlue, new Pen(Brushes.DarkBlue, 2), new Rect(20, 20, 100, 80));
// 텍스트 그리기
var ft = new FormattedText(
"Hello Visual Layer",
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Segoe UI"),
16,
Brushes.Black,
VisualTreeHelper.GetDpi(this).PixelsPerDip);
dc.DrawText(ft, new Point(30, 50));
}
// VisualCollection에 추가하여 렌더링 대상에 포함
_children.Add(visual);
}
protected override int VisualChildrenCount => _children.Count;
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count)
throw new ArgumentOutOfRangeException(nameof(index));
return _children[index];
}
}
'프로그래밍&IT > C# (Winfrom, WPF)' 카테고리의 다른 글
| [WPF] Geometries and Drawings (2) | 2025.08.09 |
|---|---|
| [WPF] Shapes, Brushes and Transforms (1) | 2025.08.09 |
| [WPF] Styles and Behaviors (1) | 2025.08.09 |
| [WPF] Resources (3) | 2025.08.07 |
| [WPF] Commands (0) | 2025.08.06 |