XAML로 시각적 계층 사용

시각적 계층 기능을 사용하는 대부분의 앱은 XAML을 사용해 기본 UI 콘텐츠를 정의합니다. Windows 10 1주년 업데이트에는 XAML 프레임워크와 시각적 계층에 이 두 기술을 더 쉽게 결합하여 멋진 사용자 환경을 만들 수 있는 새로운 기능이 있습니다. XAML 및 시각적 계층 interop 기능은 XAML API를 단독으로 사용하여 구현할 수 없는 고급 애니메이션 및 효과를 만드는 데 사용할 수 있습니다. 다음 내용이 포함됩니다.

  • 흐리고 불투명한 유리 효과 같은 브러시 효과
  • 동적 조명 효과
  • 스크롤 기반 애니메이션 및 시차
  • 자동 레이아웃 애니메이션
  • 정확한 픽셀 그림자

이러한 효과와 애니메이션은 기존 XAML 콘텐츠에 적용할 수 있으므로 새로운 기능을 활용하기 위해 XAML 앱 구조를 크게 변경하지 않아도 됩니다. 레이아웃 애니메이션, 그림자 및 흐림 효과는 아래의 레시피 섹션에서 다룹니다. 시차를 구현하는 코드 샘플은 ParallaxingListItems 샘플을 참조하세요. WindowsCompositionSamples 리포지토리에는 애니메이션, 그림자 및 효과를 구현하는 몇 가지 다른 샘플도 있습니다.

XamlCompositionBrushBase 클래스

XamlCompositionBrushCompositionBrush로 영역을 칠하는 XAML 브러시에 대한 기본 클래스를 제공합니다. 이는 쉽게 흐리고 불투명한 유리 같은 구성 효과를 XAML UI 요소에 적용하는 데 사용할 수 있습니다.

XAML UI와 브러시 사용에 대한 자세한 내용은 브러시 섹션을 참조하세요.

코드 샘플은 XamlCompositionBrushBase의 참조 페이지를 참조하세요.

XamlLight 클래스

XamlLightCompositionLight로 동적으로 영역을 밝히는 XAML 조명 효과에 대한 기본 클래스를 제공합니다.

XAML UI 요소 조명을 포함한 조명 사용에 대한 자세한 내용은 조명 섹션을 참조하세요.

코드 샘플은 XamlLight에 대한 참조 페이지를 참조하세요.

ElementCompositionPreview 클래스

ElementCompositionPreview는 XAML 및 시각적 계층 interop 기능을 제공하는 정적 클래스입니다. 시각적 계층 및 그 기능 개요는 시각적 계층을 참조하세요. ElementCompositionPreview 클래스는 다음 메서드를 제공합니다.

  • GetElementVisual: 이 요소를 렌더링하는 데 사용할 “핸드아웃” 시각적 개체를 가져옵니다.
  • SetElementChildVisual: 이 요소의 시각적 트리의 마지막 자식으로 “핸드인” 시각적 개체를 설정합니다. 이 시각적 개체는 요소의 나머지 위에 그립니다.
  • GetElementChildVisual: SetElementChildVisual을 사용하여 시각적 개체 집합 검색
  • GetScrollViewerManipulationPropertySet: ScrollViewer에서 스크롤 오프셋 기반 60fps 애니메이션을 만드는 데 사용할 수 있는 개체를 가져옵니다.

ElementCompositionPreview.GetElementVisual에 대한 설명

ElementCompositionPreview.GetElementVisual은 주어진 UIElement를 렌더링하는 데 사용되는 “유인물” 시각적 개체를 반환합니다. Visual.Opacity, Visual.OffsetVisual.Size 같은 속성은 UIElement 상태에 따라 XAML 프레임워크가 설정합니다. 이를 통해 암시적 위치 변경 애니메이션 같은 기법을 사용할 수 있습니다(레시피 참조).

오프셋크기는 XAML 프레임워크 레이아웃의 결과로 설정되므로 개발자는 이러한 속성을 수정하거나 애니메이션 효과를 주는 경우 주의해야 합니다. 개발자는 요소의 왼쪽 위 모서리가 레이아웃에서 그 부모의 위치와 동일한 위치에 있는 경우에만 오프셋을 수정하거나 애니메이션 효과를 주어야 합니다. 크기는 일반적으로 수정하면 안 되지만 이 속성에 액세스하는 것이 유용할 수 있습니다. 예를 들어, 아래 그림자 및 서리가 내린 유리 샘플은 유인물 시각적 개체의 크기를 애니메이션에 대한 입력으로 사용합니다.

또한 유인물 시각적 개체의 업데이트된 속성은 해당 UIElement에 반영되지 않습니다. 따라서 UIElement.Opacity를 0.5로 설정하면 해당 유인물 시각적 개체의 불투명도가 0.5로 설정됩니다. 그러나 유인물 시각적 개체의 불투명도를 0.5로 설정하면 콘텐츠가 50% 불투명도로 표시되지만 해당 UIElement의 불투명도 속성 값은 변경되지 않습니다.

오프셋 애니메이션의 예

오답

<Border>
      <Image x:Name="MyImage" Margin="5" />
</Border>
// Doesn’t work because Image has a margin!
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);

올바름

<Border>
    <Canvas Margin="5">
        <Image x:Name="MyImage" />
    </Canvas>
</Border>
// This works because the Canvas parent doesn’t generate a layout offset.
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);

ElementCompositionPreview.SetElementChildVisual 메서드

개발자가 ElementCompositionPreview.SetElementChildVisual을 사용하여 요소의 시각적 트리의 일부로 표시되는 “handin” 시각적 개체를 제공할 수 있습니다. 이를 통해 개발자는 XAML UI 내에 시각적 기반 콘텐츠가 나타날 수 있는 “컴퍼지션 아일랜드”를 만들 수 있습니다. 시각적 개체 기반 콘텐츠는 XAML 콘텐츠의 접근성 및 사용자 환경 보장이 동일하지 않으므로 개발자는 이 기법을 사용할 때 조심해야 합니다. 따라서 이 기법은 아래 레시피 섹션에 있는 것과 같은 사용자 지정 효과를 구현하는 데 필요한 경우에만 사용하는 것이 좋습니다.

GetAlphaMask 메서드

Image, TextBlock, Shape는 회색조 이미지로 요소의 모양을 표시하는 CompositionBrush를 반환하는 GetAlphaMask라는 메서드를 구현합니다. 이 CompositionBrush는 컴퍼지션 DropShadow에 대한 입력 역할을 할 수 있으므로 사각형 대신 요소의 모양이 그림자에 반영될 수 있습니다. 이렇게 하면 텍스트, 알파가 있는 이미지 및 모양에 픽셀에 딱 맞는 윤곽 기반 그림자를 사용할 수 있습니다. 이 API의 예는 아래의 그림자 처리를 참조하세요.

레시피

위치 변경 애니메이션

개발자는 컴퍼지션 암시적 애니메이션을 사용하여 그 부모를 기준으로 요소 레이아웃의 변경 내용에 자동으로 애니메이션 효과를 적용할 수 있습니다. 예를 들어, 아래 단추의 여백을 변경하면 자동으로 새 레이아웃 위치에 애니메이션 효과가 적용됩니다.

구현 개요

  1. 대상 요소에 대한 유인물 시각적 개체 가져오기
  2. 오프셋 속성의 변경 내용에 자동으로 애니메이션 효과를 주는 ImplicitAnimationCollection 만들기
  3. ImplicitAnimationCollection을 지원 시각적 개체와 연결
<Button x:Name="RepositionTarget" Content="Click Me" />
public MainPage()
{
    InitializeComponent();
    InitializeRepositionAnimation(RepositionTarget);
}

private void InitializeRepositionAnimation(UIElement repositionTarget)
{
    var targetVisual = ElementCompositionPreview.GetElementVisual(repositionTarget);
    Compositor compositor = targetVisual.Compositor;

    // Create an animation to animate targetVisual's Offset property to its final value
    var repositionAnimation = compositor.CreateVector3KeyFrameAnimation();
    repositionAnimation.Duration = TimeSpan.FromSeconds(0.66);
    repositionAnimation.Target = "Offset";
    repositionAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");

    // Run this animation when the Offset Property is changed
    var repositionAnimations = compositor.CreateImplicitAnimationCollection();
    repositionAnimations["Offset"] = repositionAnimation;

    targetVisual.ImplicitAnimations = repositionAnimations;
}

그림자 처리

UIElement에 픽셀에 딱 맞는 그림자를 적용합니다(예: 그림이 포함된 타원). 그림자에는 앱에서 만든 SpriteVisual이 필요하므로 ElementCompositionPreview.SetElementChildVisual을 사용하여 SpriteVisual을 포함하는 “호스트” 요소를 만들어야 합니다.

구현 개요

  1. 호스트 요소에 대한 유인물 시각적 개체 가져오기
  2. Windows.UI.Composition DropShadow 만들기
  3. 마스크를 통해 대상 요소에서 해당 셰이프를 가져오도록 DropShadow 구성
    • DropShadow 는 기본적으로 직사각형이므로 대상이 직사각형인 경우에는 필요하지 않습니다.
  4. SpriteVisual에 그림자를 연결하고 SpriteVisual을 호스트 요소의 자식으로 설정합니다.
  5. SpriteVisual의 크기를 ExpressionAnimation을 사용하는 호스트의 크기로 바인딩
<Grid Width="200" Height="200">
    <Canvas x:Name="ShadowHost" />
    <Ellipse x:Name="CircleImage">
        <Ellipse.Fill>
            <ImageBrush ImageSource="Assets/Images/2.jpg" Stretch="UniformToFill" />
        </Ellipse.Fill>
    </Ellipse>
</Grid>
public MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost, CircleImage);
}

private void InitializeDropShadow(UIElement shadowHost, Shape shadowTarget)
{
    Visual hostVisual = ElementCompositionPreview.GetElementVisual(shadowHost);
    Compositor compositor = hostVisual.Compositor;

    // Create a drop shadow
    var dropShadow = compositor.CreateDropShadow();
    dropShadow.Color = Color.FromArgb(255, 75, 75, 80);
    dropShadow.BlurRadius = 15.0f;
    dropShadow.Offset = new Vector3(2.5f, 2.5f, 0.0f);
    // Associate the shape of the shadow with the shape of the target element
    dropShadow.Mask = shadowTarget.GetAlphaMask();

    // Create a Visual to hold the shadow
    var shadowVisual = compositor.CreateSpriteVisual();
    shadowVisual.Shadow = dropShadow;

    // Add the shadow as a child of the host in the visual tree
   ElementCompositionPreview.SetElementChildVisual(shadowHost, shadowVisual);

    // Make sure size of shadow host and shadow visual always stay in sync
    var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
    bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);

    shadowVisual.StartAnimation("Size", bindSizeAnimation);
}

다음 두 가지 목록은 동일한 XAML 구조를 사용하는 이전 C# 코드의 C++/WinRTC++/CX 등가를 표시합니다.

#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <winrt/Windows.UI.Xaml.Shapes.h>
...
MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost(), CircleImage());
}

int32_t MyProperty();
void MyProperty(int32_t value);

void InitializeDropShadow(Windows::UI::Xaml::UIElement const& shadowHost, Windows::UI::Xaml::Shapes::Shape const& shadowTarget)
{
    auto hostVisual{ Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost) };
    auto compositor{ hostVisual.Compositor() };

    // Create a drop shadow
    auto dropShadow{ compositor.CreateDropShadow() };
    dropShadow.Color(Windows::UI::ColorHelper::FromArgb(255, 75, 75, 80));
    dropShadow.BlurRadius(15.0f);
    dropShadow.Offset(Windows::Foundation::Numerics::float3{ 2.5f, 2.5f, 0.0f });
    // Associate the shape of the shadow with the shape of the target element
    dropShadow.Mask(shadowTarget.GetAlphaMask());

    // Create a Visual to hold the shadow
    auto shadowVisual = compositor.CreateSpriteVisual();
    shadowVisual.Shadow(dropShadow);

    // Add the shadow as a child of the host in the visual tree
    Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);

    // Make sure size of shadow host and shadow visual always stay in sync
    auto bindSizeAnimation{ compositor.CreateExpressionAnimation(L"hostVisual.Size") };
    bindSizeAnimation.SetReferenceParameter(L"hostVisual", hostVisual);

    shadowVisual.StartAnimation(L"Size", bindSizeAnimation);
}
#include "WindowsNumerics.h"

MainPage::MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost, CircleImage);
}

void MainPage::InitializeDropShadow(Windows::UI::Xaml::UIElement^ shadowHost, Windows::UI::Xaml::Shapes::Shape^ shadowTarget)
{
    auto hostVisual = Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost);
    auto compositor = hostVisual->Compositor;

    // Create a drop shadow
    auto dropShadow = compositor->CreateDropShadow();
    dropShadow->Color = Windows::UI::ColorHelper::FromArgb(255, 75, 75, 80);
    dropShadow->BlurRadius = 15.0f;
    dropShadow->Offset = Windows::Foundation::Numerics::float3(2.5f, 2.5f, 0.0f);
    // Associate the shape of the shadow with the shape of the target element
    dropShadow->Mask = shadowTarget->GetAlphaMask();

    // Create a Visual to hold the shadow
    auto shadowVisual = compositor->CreateSpriteVisual();
    shadowVisual->Shadow = dropShadow;

    // Add the shadow as a child of the host in the visual tree
    Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);

    // Make sure size of shadow host and shadow visual always stay in sync
    auto bindSizeAnimation = compositor->CreateExpressionAnimation("hostVisual.Size");
    bindSizeAnimation->SetReferenceParameter("hostVisual", hostVisual);

    shadowVisual->StartAnimation("Size", bindSizeAnimation);
}

젖빛 유리

백그라운드 콘텐츠를 흐리게 하고 색조를 지정하는 효과를 만듭니다. 효과를 사용하려면 개발자가 Win2D NuGet 패키지를 설치해야 합니다. 설치 방법은 Win2D 홈페이지를 참조하세요.

구현 개요

  1. 호스트 요소에 대한 유인물 시각적 개체 가져오기
  2. Win2D 및 CompositionEffectSourceParameter를 사용하여 흐림 효과 트리 만들기
  3. 효과 트리를 기반으로 CompositionEffectBrush 만들기
  4. CompositionEffectBrush의 입력을 CompositionBackdropBrush로 설정하면 SpriteVisual 뒤에 있는 콘텐츠에 효과를 적용할 수 있습니다.
  5. CompositionEffectBrush를 새 SpriteVisual의 콘텐츠로 설정하고 SpriteVisual을 호스트 요소의 자식으로 설정합니다. 또는 XamlCompositionBrushBase를 사용할 수 있습니다.
  6. SpriteVisual의 크기를 ExpressionAnimation을 사용하는 호스트의 크기로 바인딩
<Grid Width="300" Height="300" Grid.Column="1">
    <Image
        Source="Assets/Images/2.jpg"
        Width="200"
        Height="200" />
    <Canvas
        x:Name="GlassHost"
        Width="150"
        Height="300"
        HorizontalAlignment="Right" />
</Grid>
public MainPage()
{
    InitializeComponent();
    InitializeFrostedGlass(GlassHost);
}

private void InitializeFrostedGlass(UIElement glassHost)
{
    Visual hostVisual = ElementCompositionPreview.GetElementVisual(glassHost);
    Compositor compositor = hostVisual.Compositor;

    // Create a glass effect, requires Win2D NuGet package
    var glassEffect = new GaussianBlurEffect
    { 
        BlurAmount = 15.0f,
        BorderMode = EffectBorderMode.Hard,
        Source = new ArithmeticCompositeEffect
        {
            MultiplyAmount = 0,
            Source1Amount = 0.5f,
            Source2Amount = 0.5f,
            Source1 = new CompositionEffectSourceParameter("backdropBrush"),
            Source2 = new ColorSourceEffect
            {
                Color = Color.FromArgb(255, 245, 245, 245)
            }
        }
    };

    //  Create an instance of the effect and set its source to a CompositionBackdropBrush
    var effectFactory = compositor.CreateEffectFactory(glassEffect);
    var backdropBrush = compositor.CreateBackdropBrush();
    var effectBrush = effectFactory.CreateBrush();

    effectBrush.SetSourceParameter("backdropBrush", backdropBrush);

    // Create a Visual to contain the frosted glass effect
    var glassVisual = compositor.CreateSpriteVisual();
    glassVisual.Brush = effectBrush;

    // Add the blur as a child of the host in the visual tree
    ElementCompositionPreview.SetElementChildVisual(glassHost, glassVisual);

    // Make sure size of glass host and glass visual always stay in sync
    var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
    bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);

    glassVisual.StartAnimation("Size", bindSizeAnimation);
}

추가 리소스