NET MAUI - XAML 스타일을 사용하여 일관된 UI 만들기

2022. 7. 18. 00:00MAUI

반응형

리소스는 XAML 태그에서 하드 코딩된 중복 값을 방지하는 데 유용하지만 

각 속성 값을 개별적으로 할당하면 복잡하고 장황한 XAML이 생성될 수 있다.

스타일을 사용하면 여러 설정을 스타일로 그룹화하여 코드를 깔끔하게 정리 하고 유지보수 할 수 있다.

 

리소스를 사용할 때의 문제점 

<Button
    Text = "OK"
    BackgroundColor = "{StaticResource highlightColor}"
    BorderColor = "{StaticResource borderColor}"
    BorderWidth = "{StaticResource borderWidth}"
    TextColor = "{StaticResource textColor}" />

<Button
    Text = "Cancel"
    BackgroundColor = "{StaticResource highlightColor}"
    BorderColor = "{StaticResource borderColor}"
    BorderWidth = "{StaticResource borderWidth}"
    TextColor = "{StaticResource textColor}" />

여러 속성에 각각의 resource 를 사용하다 보니 XAML 이 읽기 어려워 졌다.

많은 수의 속성을 설정하는 경우 실수로 속성 중 하나를 생략하여 컨트롤 모양이 다를 수 있다.

해결책은 한번에 네 가지 속성을 모두 할당하는 스타일을 만드는 것이다.

 

Setter 란 무엇인가?

: 스타일을 만드는 데 사용하는 주요 구성 요소

다음 예제에서는 TextColor 속성에 대한 Setter 객체를 만든다.

<Setter Property="TextColor" Value="White" />

또한 Setter 에는 Resource 를 사용 할 수 있다. 

<Setter Property="TextColor" Value="{StaticResource textColor}" />

** Setter 에 지정하는 속성값은 binding 가능한 속성으로 구현되어야 한다. **

** TextColor 은 TextColorProperty 라는 바인딩 가능한 속성이 있다. **

 

Style 이란 무엇인가?

스타일은 특정 유형의 컨트롤을 대상으로 하는 setter 모음이다. 

<Style TargetType="Button">
    <Setter Property="BackgroundColor" Value="#2A84D3" />
    <Setter Property="BorderColor" Value="#1C5F9B" />
    <Setter Property="BorderWidth" Value="3" />
    <Setter Property="TextColor" Value="White" />
</Style>

위 코드는 Button 에 아래 4가지 속성을 지정한다. 

또한 제일 처음 문제가 되었던 많은 수의 Resource 문제를 해결해 준다. 

 

Style 정의

일반적으로 ResourceDictionary 개체 내부의 리소스로 스타일을 정의한다. 

다음 코드는 스타일을 사전 내부의 리소스로 정의하는 방법을 보여준다.

<ContentPage.Resources>
    <Style x:Key="MyButtonStyle" TargetType="Button">
        ...
    </Style>
</ContentPage.Resources>

스타일에는 x:Key 속성을 사용하여 이름이 지정된다.

스타일 이름을 지정하면 XAML 페이지 내에서 스타일을 참조할 수 있다.

 

Style 적용

Style 속성에 이름을 지정하여 컨트롤에 스타일을 연결한다.

할당으로 인해 스타일의 각 Setter 개체가 대상 컨트롤에 적용된다. 

<Button Text="OK" Style="{StaticResource MyButtonStyle}" />
<Button Text="Cancel" Style="{StaticResource MyButtonStyle}" />

StaticResource 는 이전에도 이야기 했듯이 runtime 에 변경이 불가능하다.

만약 runtime 에 테마를 동적으로 변경해야 하는 경우 DynamicResource 를 이용하여 Style 을 로드하자.

 

여러 컨트롤에 암시적 스타일 사용

하나의 컨트롤이 아닌 같은 type 의 여러 컨트롤에 같은 Style 을 적용하려 할 수 있다. 

이럴 경우 이전 방식을 사용하면 (x:Key 가 정의된) 모든 Button 에 Style 에 할당해야 할것이다. 

너무 많은 반복작업과 중복 코드가 생긴다. 

그럴경우 아래와 같이 x:Key 를 삭제하여 암시적 스타일을 사용하게 만들자

<ContentPage.Resources>
    <Style TargetType="Button">
        <Setter Property="BackgroundColor" Value="Blue" />
        <Setter Property="BorderColor" Value="Navy" />
        ...
    </Style>
</ContentPage.Resources>

해당 ContentPage 의 모든 Button 은 Style 이 자동으로 적용된다. 

 

암시적 스타일을 컨트롤에 일치시키려면 지정된 TargetType과 정확히 일치해야 한다.

대상 유형에서 상속되는 컨트롤은 스타일을 수신하지 않는다. 

상속된 컨트롤에도 적용하려면 Style.ApplyToDerivedTypes 특성을 True로 설정해야한다. 

예를 들어, Button 유형에 스타일을 적용하고 Button에서 상속하는 모든 버튼(예: ImageButton, RadioButton 또는 사용자가 만든 사용자 정의 유형)에 영향을 미치도록 하려면 다음과 같은 스타일을 사용할 수 있다.

<ContentPage.Resources>
    <Style TargetType="Button"
           ApplyToDerivedTypes="True">
        <Setter Property="BackgroundColor" Value="Black" />
    </Style>
</ContentPage.Resources>

Style 재정의 (override a style)

Style  의 값중 특정 속성을 다른 값으로 사용하고 싶다면 아래와 같이 사용할 수 있다. 

<Style x:Key="MyButtonStyle" TargetType="Button">
    <Setter Property="BackgroundColor" Value="Blue" />
    <Setter Property="BorderRadius" Value="10" />
    <Setter Property="BorderWidth" Value="3" />
</Style>


<Button
    Text="Cancel"
    Style="{StaticResource MyButtonStyle}"
    BackgroundColor="Red"
    ... />

BackgroudColor 를 Style 에서 정의한 Blue 가 아닌 Red 를 사용하도록 재정의 한다. 

 

상위 유형 지정 (Target an ancestor type)

 

Button 과 Label 에 사용자 정의 배경색을 원한다고 하자.

그 상위 유형인 VisualElement 를 TargetType 으로 하여 스타일을 만들어 사용하면 모두 적용가능하다.

<Style x:Key="MyVisualElementStyle" TargetType="VisualElement">
    <Setter Property="BackgroundColor" Value="#2A84D3" />
</Style>
...
<Button Style="{StaticResource MyVisualElementStyle}" ... />
<Label Style="{StaticResource MyVisualElementStyle}" ... />

암시적 스타일을 쓰면 더 편할 것이라 생각 할수 있다.

하지만  암시적 스타일은 TargetType 이 컨트롤 Type 과 정확히 일치해야 하므로 동작하지 않는다.

 

BasedOn 을 사용하여 Style 상속

다음과 같이 Entry 와 Button 각각에 style 을 정의 했다고 가정하자

<Style x:Key="MyButtonStyle" TargetType="Button">
    <Setter Property="BackgroundColor" Value="Blue" />
    <Setter Property="BorderColor" Value="Navy" />
    <Setter Property="BorderWidth" Value="5" />
</Style>

<Style x:Key="MyEntryStyle" TargetType="Entry">
    <Setter Property="BackgroundColor" Value="Blue" />
    <Setter Property="TextColor" Value="White" />
</Style>

그런데 두 style 을 보면 중복되는 부분이 있다. 

이럴 경우 style 상속을 사용하여 중복 되는 부분을 기본 style 로 분리 할 수 있다. 

<Style x:Key="MyVisualElementStyle" TargetType="VisualElement">
    <Setter Property="BackgroundColor" Value="Blue" />
</Style>

<Style x:Key="MyButtonStyle" TargetType="Button" BasedOn="{StaticResource MyVisualElementStyle}">
    <Setter Property="BorderColor" Value="Navy" />
    <Setter Property="BorderWidth" Value="5" />
</Style>

<Style x:Key="MyEntryStyle" TargetType="Entry" BasedOn="{StaticResource MyVisualElementStyle}">
    <Setter Property="TextColor" Value="White" />
</Style>

BasedOn 을 이용하여 style 을 상속받은 두개의 Style 이 있다. 

BasedOn 에서는 StaticResource 만 사용 가능 하다. 

 

예제) 이전 강좌에서 사용했던 예제를 이어서 사용한다. 

MainPage.xaml 을 열자

ResourceDictionary 아래에 Style 을 추가 하자

<ContentPage.Resources>
    <ResourceDictionary>
        ...
        <Style x:Key="infoLabelStyle" TargetType="Label">
        </Style>
    </ResourceDictionary>
</ContentPage.Resources>

Setter 를 추가 하자

<Style x:Key="infoLabelStyle" TargetType="Label">
    <Setter Property="TextColor" Value="{StaticResource fgColor}" />
    <Setter Property="FontSize" Value="{StaticResource fontSize}" />
    <Setter Property="FontAttributes" Value="Bold" />
</Style>

 

스타일 적용

billLabel, tipLabel, totalLabel 에 속성 값들에 style 을 적용하자

<!-- Left column = static labels -->
<Label x:Name="billLabel"  Text="Bill"  ... Style="{StaticResource infoLabelStyle}" ... />
<Label x:Name="tipLabel"   Text="Tip"   ... Style="{StaticResource infoLabelStyle}" ... />
<Label x:Name="totalLabel" Text="Total" ... Style="{StaticResource infoLabelStyle}" ... />

tipOutput 과 totalOutput 을 확인하면 FontAttribute = "Bold" 를 제외하면 infoLabelStyle 과 같다. 

자 그렇다면 BasedOn 을 이용하여 상속해보자

 

스타일 상속을 사용하여 다시 구현하기

<Style x:Key="baseLabelStyle" TargetType="Label">
    <Setter Property="TextColor" Value="{StaticResource fgColor}" />
    <Setter Property="FontSize" Value="{StaticResource fontSize}" />
</Style>
<Style x:Key="infoLabelStyle" TargetType="Label" BasedOn="{StaticResource baseLabelStyle}">
    <Setter Property="FontAttributes" Value="Bold" />
</Style>
<!-- Left column = static labels -->
<Label x:Name="billLabel"  Text="Bill"  
       Style="{StaticResource infoLabelStyle}"
       Grid.Row="0" Grid.Column="0" />
<Label x:Name="tipLabel"   Text="Tip"   
       Style="{StaticResource infoLabelStyle}"
       Grid.Row="1" Grid.Column="0" />
<Label x:Name="totalLabel" Text="Total" 
       Style="{StaticResource infoLabelStyle}"
       Grid.Row="2" Grid.Column="0" />

<!-- Right column = user input and calculated-value output -->
<Entry x:Name="billInput"   Placeholder="Enter Amount" 
       Keyboard="Numeric" TextColor="Gray" Grid.Row="0" Grid.Column="1" />
<Label x:Name="tipOutput"   Text="0.00" 
       Style="{StaticResource baseLabelStyle}" 
       Grid.Row="1" Grid.Column="1" />
<Label x:Name="totalOutput" Text="0.00" 
       Style="{StaticResource baseLabelStyle}"
       Grid.Row="2" Grid.Column="1" />

 

 

관련영상

https://youtu.be/WlhqKj3s-do

반응형