XAML을 사용한 반응형 레이아웃
XAML 레이아웃 시스템은 반응형 UI를 만드는 데 도움이 되도록 요소, 레이아웃 패널 및 시각적 상태의 자동 크기 조정을 제공합니다. 반응형 레이아웃을 사용하면 다양한 앱 창 크기, 해상도, 픽셀 밀도 및 방향의 화면에서 앱의 모양을 멋지게 표현할 수 있습니다. 또한 XAML을 사용하면 반응형 디자인 기술에서 설명한 것처럼 앱 UI의 위치 및 크기 조정, 재배치, 표시/숨기기, 변경 또는 재설계가 가능합니다. 여기에서는 XAML을 사용하여 반응형 레이아웃을 구현하는 방법에 대해서 설명하겠습니다.
속성 및 패널을 통한 유동 레이아웃
반응형 레이아웃의 기초는 콘텐츠의 유동적인 위치 변경, 크기 조정 및 재배치를 위해 XAML 레이아웃 속성 및 패널을 적절하게 사용하는 것입니다.
XAML 레이아웃 시스템은 정적 레이아웃과 유동 레이아웃을 모두 지원합니다. 정적 레이아웃에서는 컨트롤에 명시적 픽셀 크기 및 위치를 제공합니다. 사용자가 디바이스의 해상도 또는 방향을 변경하면 UI가 변경되지 않습니다. 정적 레이아웃은 다양한 폼 팩터 및 디스플레이 크기에 걸쳐 클리핑될 수 있습니다. 반면에 유동 레이아웃은 디바이스에서 사용할 수 있는 가시 공간에 맞게 축소, 확대 또는 재배치됩니다.
실제로 정적 요소와 유동 요소의 조합을 사용하여 UI를 만듭니다. 일부 위치에서 여전히 고정 요소와 값을 사용할 수 있지만, 전체 UI는 해상도, 화면 크기 및 보기에 따라 반응할 수 있도록 해야 합니다.
여기서는 XAML 속성 및 레이아웃 패널을 사용하여 유동 레이아웃을 만드는 방법을 살펴봅니다.
레이아웃 속성
레이아웃 속성은 요소의 크기 및 위치를 제어합니다. 유동 레이아웃을 만들려면 요소에 대해 자동 또는 가변 크기 조정을 사용하고 레이아웃 패널에서 필요에 따라 자식의 위치를 지정하도록 허용합니다.
다음은 몇 가지 공통 레이아웃 속성과 이러한 속성을 사용하여 유동 레이아웃을 만드는 방법입니다.
Height 및 Width
Height 및 Width 속성은 요소의 크기를 지정합니다. 유효 픽셀로 측정된 고정 값을 사용하거나, 자동 또는 가변 크기 조정을 사용할 수 있습니다.
자동 크기 조정은 해당 콘텐츠 또는 부모 컨테이너에 맞게 UI 요소의 크기를 조정합니다. 표의 행과 열에 자동 크기 조정을 사용할 수도 있습니다. 자동 크기 조정을 사용하려면 UI 요소의 높이 및/또는 너비를 Auto로 설정합니다.
참고 항목
요소의 크기가 콘텐츠 또는 컨테이너 중에서 어떤 것에 맞게 조정될지는 부모 컨테이너가 자식의 크기 조정을 처리하는 방식에 따라 결정됩니다. 자세한 내용은 이 문서의 뒷부분에 나오는 레이아웃 패널을 참조하세요.
배율 크기 조정이라고도 하는 가변 크기 조정은 가중 비율에 따라 그리드의 행과 열 사이에서 사용 가능한 공간을 분배합니다. XAML에서는 배율 값이 *(또는 가중 배율 크기 조정의 경우 n*)로 표현됩니다. 예를 들어 2열 레이아웃에서 첫 번째 열을 두 번째 열보다 5배 너비로 지정하려면 ColumnDefinition 요소에서 Width 속성에 대해 "5*" 및 "*"를 사용합니다.
이 예제에서는 Grid에서 고정, 자동 및 비례 크기 조정을 4개의 열과 결합합니다.
Column | 크기 조정 | 설명 |
---|---|---|
Column_1 | 자동 | 열의 크기가 콘텐츠에 맞게 조정됩니다. |
Column_2 | * | 자동 열을 계산한 후 열은 다시 기본 너비의 일부를 가져옵니다. Column_2는 Column_4의 절반 정도 너비가 될 것입니다. |
Column_3 | 44 | 열 너비는 44픽셀입니다. |
Column_4 | 2* | 자동 열을 계산한 후 열은 다시 기본 너비의 일부를 가져옵니다. Column_4는 Column_2보다 두 배 넓습니다. |
기본 열 너비는 "*"이므로 두 번째 열에 대해 이 값을 명시적으로 설정할 필요가 없습니다.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="44"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Column 1 sizes to its content." FontSize="24"/>
</Grid>
Visual Studio XAML 디자이너에서 결과는 다음과 같습니다.
런타임 시 요소의 크기를 가져오려면 Height와 Width 대신 읽기 전용 ActualHeight와 ActualWidth 속성을 사용합니다.
크기 제약 조건
UI에서 자동 크기 조정을 사용하는 경우에도 요소 크기에 제약 조건을 적용해야 할 수 있습니다. MinWidth/MaxWidth 및 MinHeight/MaxHeight는 유동 크기 조정을 허용하면서 요소의 크기를 제한하는 값을 지정합니다.
Grid에서 MinWidth/MaxWidth를 열 정의와 함께 사용할 수도 있으며 MinHeight/MaxHeight는 행 정의와 함께 사용할 수 있습니다.
맞춤
HorizontalAlignment 및 VerticalAlignment 속성은 해당 부모 컨테이너 내에서 요소의 위치가 결정되는 방법을 지정합니다.
- HorizontalAlignment의 값은 Left, Center, Right, Stretch입니다.
- VerticalAlignment의 값은 Top, Center, Bottom, Stretch입니다.
Stretch 맞춤을 사용하면 요소가 부모 컨테이너에 제공된 모든 공간을 채웁니다. Stretch는 두 맞춤 속성의 기본값입니다. 그러나 Button과 같은 일부 컨트롤은 기본 스타일에서 이 값을 재정의합니다. 자식 요소를 가질 수 있는 모든 요소는 HorizontalAlignment 및 VerticalAlignment 속성의 Stretch 값을 고유하게 처리할 수 있습니다. 예를 들어 Grid에 배치된 기본 Stretch 값을 사용하는 요소가 확장되어 포함된 셀을 채웁니다. Canvas 크기에 해당 콘텐츠에 배치된 동일한 요소입니다. 각 패널이 Stretch 값을 처리하는 방법에 대한 자세한 내용은 레이아웃 패널 문서를 참조하세요.
자세한 내용은 맞춤, 여백 및 안쪽 여백 문서 및 HorizontalAlignment, VerticalAlignment 참조 페이지를 참조하세요.
표시
해당 Visibility 속성을 Visibility 열거형 값 중 하나인 Visible 또는 Collapsed로 요소를 표시하거나 숨길 수 있습니다. 요소가 Collapsed이면 UI 레이아웃에서 공간을 차지하지 않습니다.
코드 또는 시각적 상태에서 요소의 Visibility 속성을 변경할 수 있습니다. 요소의 Visibility가 변경되면 모든 자식 요소도 변경됩니다. 다른 패널을 축소하는 동안 한 패널을 표시하여 UI의 섹션을 바꿀 수 있습니다.
팁
기본적으로 Collapsed인 요소가 UI에 있는 경우 비록 보이지 않아도 시작할 때 해당 개체는 여전히 만들어집니다. x:Load 특성을 사용하여 개체 생성을 지연시키고 이러한 요소가 표시될 때까지 로드를 연기할 수 있습니다. 이렇게 하면 시작 성능이 향상될 수 있습니다. 자세한 내용은 x:Load 특성을 참조하세요.
스타일 리소스
컨트롤에서 각 속성 값을 개별적으로 설정할 필요는 없습니다. 일반적으로 속성 값을 Style 리소스로 그룹화하고 컨트롤에 Style을 적용하는 것이 더 효율적입니다. 이는 많은 컨트롤에 동일한 속성 값을 적용해야 하는 경우에 특히 효율적입니다. 컨트롤에 스타일을 지정하는 방법에 대한 자세한 내용은 컨트롤 스타일링을 참조하세요.
레이아웃 패널
시각적 개체의 위치를 지정하려면 패널 또는 다른 컨테이너 개체에 배치해야 합니다. XAML 프레임워크는 Canvas, Grid, RelativePanel, StackPanel과 같은 다양한 패널 클래스를 제공합니다. 이 클래스는 컨테이너 역할을 하며 그 안에 UI 요소를 배치하고 정렬할 수 있습니다.
레이아웃 패널을 선택할 때 고려해야 할 기본 사항은 패널이 자식 요소의 위치를 지정하고 크기를 조정하는 방법입니다. 또한 겹치는 자식 요소가 서로 겹쳐지는 방식도 고려해야 할 수도 있습니다.
다음은 XAML 프레임워크에서 제공되는 패널 컨트롤의 기본 기능을 비교한 것입니다.
패널 컨트롤 | 설명 |
---|---|
캔버스 | Canvas는 유체 UI를 지원하지 않습니다. 자식 요소의 위치 지정 및 크기 조정의 모든 측면을 제어합니다. 일반적으로 그래픽을 만들거나 더 큰 적응형 UI의 작은 정적 영역을 정의하는 것과 같이 특수한 경우에 사용합니다. 코드 또는 시각적 상태를 사용하여 런타임에 요소의 위치를 변경할 수 있습니다. |
그리드 | Grid는 자식 요소의 유동 크기 조정을 지원합니다. 코드 또는 시각적 상태를 사용하여 요소의 위치를 변경하고 재배치할 수 있습니다. |
RelativePanel | |
StackPanel | |
VariableSizedWrapGrid |
이러한 패널에 대한 자세한 내용 및 예제는 레이아웃 패널을 참조하세요.
레이아웃 패널을 사용하면 UI를 논리적 컨트롤 그룹으로 구성할 수 있습니다. 적절한 속성 설정과 함께 사용하는 경우 UI 요소의 자동 크기 조정, 위치 변경 및 재배치에 대한 지원을 받을 수 있습니다. 단, 창 크기가 상당히 변경되면 대부분의 UI 레이아웃을 추가로 수정해야 합니다. 이를 위해 시각적 상태를 사용할 수 있습니다.
시각적 상태 및 상태 트리거를 사용한 적응형 레이아웃
시각적 상태를 사용하여 창 크기 또는 기타 변경 사항에 따라 UI를 크게 변경할 수 있습니다.
앱 창이 일정 용량을 초과하여 증가하거나 축소되면 레이아웃 속성을 변경하여 UI 섹션의 위치를 변경, 크기 조정, 재배치, 표시 또는 바꿀 수 있습니다. UI에 대한 다양한 시각적 상태를 정의하고 창 너비 또는 창 높이가 지정된 임곗값을 초과할 때 적용할 수 있습니다.
VisualState는 특정 상태일 때 요소에 적용되는 속성 값을 정의합니다. 지정된 조건이 충족될 때 적절한 VisualState를 적용하는 VisualStateManager의 시각적 상태를 그룹화합니다. AdaptiveTrigger는 XAML에서 상태가 적용되는 임계값('중단점'이라고도 함)을 설정하는 쉬운 방법을 제공합니다. 또는 코드에서 VisualStateManager.GoToState 메서드를 호출하여 시각적 상태를 적용할 수 있습니다. 두 가지 방법의 예제는 다음 섹션에 나와 있습니다.
코드에서 시각적 상태 설정
코드에서 VisualStateManager.GoToState 메서드를 호출하여 시각적 상태를 적용할 수 있습니다. 예를 들어 앱 창이 특정 크기일 때 상태를 적용하려면 SizeChanged 이벤트를 처리하고 GoToState를 호출하여 적절한 상태를 적용합니다.
여기서는 VisualStateGroup에 2개의 VisualState 정의가 포함되어 있습니다. 최초인 DefaultState
이(가) 비어 있습니다. 적용되면 XAML 페이지에 정의된 값이 적용됩니다. 두 번째 정의인 WideState
은(는) SplitView의 DisplayMode 속성을 Inline으로 변경하고 창을 엽니다. 이 상태는 창 너비가 640 유효 픽셀 이상인 경우 SizeChanged 이벤트 처리기에서 적용됩니다.
참고 항목
Windows에서는 앱이 실행되는 특정 디바이스를 앱 내에서 검색하는 방법을 제공하지 않습니다. 앱이 실행 중인 디바이스 패밀리(데스크톱 등), 효과적인 해상도 및 앱에서 사용할 수 있는 화면 공간의 양(앱 창 크기)을 알 수 있습니다. 따라서 화면 크기 및 중단점에 대해 시각적 상태를 정의하는 것이 좋습니다.
<Page ...
SizeChanged="CurrentWindow_SizeChanged">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="DefaultState">
<Storyboard>
</Storyboard>
</VisualState>
<VisualState x:Name="WideState">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="SplitView.DisplayMode"
Storyboard.TargetName="mySplitView">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<SplitViewDisplayMode>Inline</SplitViewDisplayMode>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="SplitView.IsPaneOpen"
Storyboard.TargetName="mySplitView">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<SplitView x:Name="mySplitView" DisplayMode="CompactInline"
IsPaneOpen="False" CompactPaneLength="20">
<!-- SplitView content -->
<SplitView.Pane>
<!-- Pane content -->
</SplitView.Pane>
</SplitView>
</Grid>
</Page>
private void CurrentWindow_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
if (e.Size.Width > 640)
VisualStateManager.GoToState(this, "WideState", false);
else
VisualStateManager.GoToState(this, "DefaultState", false);
}
// YourPage.h
void CurrentWindow_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs const& e);
// YourPage.cpp
void YourPage::CurrentWindow_SizeChanged(IInspectable const& sender, SizeChangedEventArgs const& e)
{
if (e.NewSize.Width > 640)
VisualStateManager::GoToState(*this, "WideState", false);
else
VisualStateManager::GoToState(*this, "DefaultState", false);
}
XAML 마크업에서 시각적 상태 설정
Windows 10 이전에는 VisualState 정의에 속성 변경에 Storyboard 개체가 필요했으며, 상태를 적용하기 위해 코드에서 GoToState 를 호출해야 했습니다. 위의 예에도 이러한 열이 포함되어 있습니다. 이 구문을 사용하는 많은 예제가 계속 표시되거나 이 구문을 사용하는 기존 코드가 있을 수 있습니다.
Windows 10부터 여기에 표시된 간소화된 Setter 구문을 사용할 수 있으며, XAML 태그에서 StateTrigger를 사용하여 상태를 적용할 수 있습니다. 상태 트리거를 사용하여 앱 이벤트에 대한 응답으로 시각적 상태 변경을 자동으로 트리거하는 간단한 규칙을 만들 수 있습니다.
이 예제에서는 이전 예제와 동일한 작업을 수행하지만 Storyboard 대신 간소화된 Setter 구문을 사용하여 속성 변경 내용을 정의합니다. 또한 GoToState를 호출하는 대신 기본 제공되는 AdaptiveTrigger 상태 트리거를 사용하여 상태를 적용합니다. 상태 트리거를 사용하는 경우 빈 DefaultState
트리거를 정의할 필요가 없습니다. 상태 트리거의 조건이 더 이상 충족되지 않는 경우 기본 설정이 자동으로 다시 적용됩니다.
<Page ...>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!-- VisualState to be triggered when the
window width is >=640 effective pixels. -->
<AdaptiveTrigger MinWindowWidth="640" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="mySplitView.DisplayMode" Value="Inline"/>
<Setter Target="mySplitView.IsPaneOpen" Value="True"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<SplitView x:Name="mySplitView" DisplayMode="CompactInline"
IsPaneOpen="False" CompactPaneLength="20">
<!-- SplitView content -->
<SplitView.Pane>
<!-- Pane content -->
</SplitView.Pane>
</SplitView>
</Grid>
</Page>
Important
이전 예제에서는 VisualStateManager.VisualStateGroups 연결된 속성이 Grid 요소에 설정되었습니다. StateTriggers를 사용하는 경우 트리거가 자동으로 적용되도록 항상 VisualStateGroups가 루트의 첫 번째 자식에 연결되어 있는지 확인합니다. (여기서 Grid는 루트 Page 요소의 첫 번째 자식입니다.)
첨부된 속성 구문
VisualState에서는 일반적으로 컨트롤 속성 또는 컨트롤이 포함된 패널에 첨부된 속성 중 하나에 대한 값을 설정합니다. 첨부된 속성을 설정할 때 첨부된 속성 이름 주위에 괄호를 사용합니다.
이 예제에서는 이름이 myTextBox
인 TextBox에서 RelativePanel.AlignHorizontalCenterWithPanel에 첨부된 속성을 설정하는 방법을 보여 줍니다. 첫 번째 XAML은 ObjectAnimationUsingKeyFrames 구문을 사용하고 두 번째 XAML은 Setter 구문을 사용합니다.
<!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
Storyboard.TargetName="myTextBox">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
<!-- Set an attached property using Setter. -->
<Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
사용자 지정 상태 트리거
StateTrigger 클래스를 확장하여 광범위한 시나리오에 대한 사용자 지정 트리거를 만들 수 있습니다. 예를 들어 입력 형식에 따라 다른 상태를 트리거하는 StateTrigger를 만든 다음 입력 형식을 누를 때 컨트롤 주위의 여백을 늘릴 수 있습니다. 또는 StateTrigger를 생성하여 앱이 실행되는 디바이스 패밀리에 따라 다른 상태를 적용합니다. 사용자 지정 트리거를 빌드하고 이를 사용하여 단일 XAML 보기 내에서 최적화된 UI 환경을 만드는 방법에 대한 예제는 상태 트리거 예제를 참조하세요.
시각적 상태 및 스타일
시각적 상태의 Style 리소스를 사용하여 여러 컨트롤에 속성 변경 내용 집합을 적용할 수 있습니다. 컨트롤에 스타일을 지정하는 방법에 대한 자세한 내용은 컨트롤 스타일링을 참조하세요.
상태 트리거 예제에서 이 간소화된 XAML에서는 단추에 스타일 리소스를 적용하여 마우스 또는 터치 입력의 크기와 여백을 조정합니다. 전체 코드 및 사용자 지정 상태 트리거의 정의는 상태 트리거 예제를 참조하세요.
<Page ... >
<Page.Resources>
<!-- Styles to be used for mouse vs. touch/pen hit targets -->
<Style x:Key="MouseStyle" TargetType="Rectangle">
<Setter Property="Margin" Value="5" />
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="20" />
</Style>
<Style x:Key="TouchPenStyle" TargetType="Rectangle">
<Setter Property="Margin" Value="15" />
<Setter Property="Height" Value="40" />
<Setter Property="Width" Value="40" />
</Style>
</Page.Resources>
<RelativePanel>
<!-- ... -->
<Button Content="Color Palette Button" x:Name="MenuButton">
<Button.Flyout>
<Flyout Placement="Bottom">
<RelativePanel>
<Rectangle Name="BlueRect" Fill="Blue"/>
<Rectangle Name="GreenRect" Fill="Green" RelativePanel.RightOf="BlueRect" />
<!-- ... -->
</RelativePanel>
</Flyout>
</Button.Flyout>
</Button>
<!-- ... -->
</RelativePanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="InputTypeStates">
<!-- Second set of VisualStates for building responsive UI optimized for input type.
Take a look at InputTypeTrigger.cs class in CustomTriggers folder to see how this is implemented. -->
<VisualState>
<VisualState.StateTriggers>
<!-- This trigger indicates that this VisualState is to be applied when MenuButton is invoked using a mouse. -->
<triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Mouse" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BlueRect.Style" Value="{StaticResource MouseStyle}" />
<Setter Target="GreenRect.Style" Value="{StaticResource MouseStyle}" />
<!-- ... -->
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<!-- Multiple trigger statements can be declared in the following way to imply OR usage.
For example, the following statements indicate that this VisualState is to be applied when MenuButton is invoked using Touch OR Pen.-->
<triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Touch" />
<triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Pen" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BlueRect.Style" Value="{StaticResource TouchPenStyle}" />
<Setter Target="GreenRect.Style" Value="{StaticResource TouchPenStyle}" />
<!-- ... -->
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Page>
관련 항목
Windows developer