[C#/WPF] Custom Button Style

용어를 뭐라고 해야할지 모르겠어서 막 써보면 커스텀 스타일은 리소스에 정의를 하고, x:Key가 존재하면 해당 키를 스타일로 참조하는 컨트롤만 적용이되고, 키를 정의하지 않으면 해당 컨트롤의 하위의 모든 컨트롤에 적용된다. 가령 다음과 같은 그리드에 버튼을 만든다고 한다.

간단히 만들어보기

[code language=”xml” gutter=”false”]
<Grid> … </Grid>
[/code]

다음과 같은 버튼 두개를 만든다

[code language=”xml” title=”Code 1″]
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Button Name="TestButton1" Grid.Row="1" Grid.Column="0">Test1</Button>
<Button Name="TestButton2" Grid.Row="1" Grid.Column="2">Test2</Button>
</Grid>
[/code]

버튼에 스타일을 적용하기 위해서는 버튼의 상위인 그리드나 윈도우(혹은 유저컨트롤)의 리소스에 등록해야한다. 그리드에 정의를 하기 위해서는 그리드의 리소스에 스타일을 등록해야한다.

[code language=”xml” title=”Code 2″]
<Grid>
<Grid.Resources>
… <!– 이곳에 스타일을 정의해서 넣어야함 –>
<Grid.Resources>
… <!– 위의 Code 1 참조 –>
<Grid>
[/code]

버튼(뿐 아니라 다른 컨트롤들도)의 스타일은 다음과 같이 정의된다.

[code language=”xml” title=”Style 1″]
<Style TargetType="Button">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
[/code]

가장 안쪽에 있는 ContentPresenter은 실제 버튼 사용시 사용되는 문자열(Code 1에서 Test1, Test2 따위)를 나타낸다. 실제 스타일은 안에 정의한다. 다른 컨트롤들은 TargetType을 바꾸면 된다. 이 ContentPresenter를 이용하면 여러가지 일을 할 수 있다. 는 응용에서 보도록 하고…
일단 테스트로 간단한 스타일을 지정해보도록 하자. 이번에 해볼 것은 알약(Pill)모양의 버튼이다.

[code language=”xml” title=”Style 2″]
<Style TargetType="Button">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="Blue" Background="Blue" CornerRadius="5" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Foreground="White">
<ContentPresenter />
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
[/code]

위와같은 스타일로 지정을 해놓는다면 다음과 같은 버튼이 생성된다.

[code language=”xml” title=”Code 1, Code 2, Style 2″ collapse=”true”]
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="Blue" Background="Blue" CornerRadius="5" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="10">
<TextBlock Foreground="White">
<ContentPresenter />
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</Grid.Resources>

<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<Button Name="TestButton1" Grid.Row="1" Grid.Column="0">
Test1
</Button>
<Button Name="TestButton2" Grid.Row="1" Grid.Column="2">
Test2
</Button>
</Grid>
[/code]

Trigger

트리거란 어떤 컨트롤의 프로퍼티가 지정해둔 값이 되었을 때, 다른 스타일이나 프로퍼티의 값을 바꾸는 것이다. 가령, 위에서 만든 버튼에서 마우스가 버튼 위에 있을 때, 배경의 색과 테두리 색을 빨간색으로 바꾼다고 하자.
이때 트리거는 ControlTemplate.Triggers 내부에 정의해야한다. 그런데, 이벤트는 버튼에서 일어나지만, 실제로 바뀌는 프로퍼티는 Border의 프로퍼티이다. 따라서 타겟네임을 정할때에, Border를 알아내기 위해서, Border의 Name을 정해두어야한다.

[code language=”xml” title=”Trigger”]
<ControlTemplate TargetName="Button">
<!– 디자인 요소 –>
<Border Name="Border" …>

</Border>
<ControlTemplate.Triggers>
<!– 트리거 –>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="Red" />
<Setter TargetName="Border" Property="BorderBrush" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
[/code]

위에서는 IsMouseOver (해당 컨트롤의 위에 마우스 커서가 위치하는 지 여부를 반환하는 프로퍼티)가 True일 때 Border의 Background와 BorderBrush를 Red(SolidColorBrush)로 설정한다.
그래서 마우스 커서를 올려놓으면 다음과 같이 붉은색으로 변한다.

Event Trigger, Storyboard, Animation

이벤트 트리거는 지정한 이벤트가 일어났을 때 작동하는 트리거를 말한다. 가령, 이벤트트리거의 RoutedEvent 속성을 MouseEnter로 해놓는다면, 해당 컨트롤에 마우스 커서가 밖에서 들어오면 트리거가 실행된다. 이 트리거에 스토리보드를 설정해놓는다면, 해당 스토리보드가 실행된다.
스토리보드란 어떤 이벤트가 일어났을 때부터 보여지는 일련의 애니메이션 집합이다.
애니메이션이란 말 그대로 애니메이션이다 (…) 가령 컨트롤같은게 이동한다거나, 색이 변한다거나 투명도가 변한다거나 하는 것을 쉽게(?) 만들어주는 것이 애니메이션이다.

아까 추가했던 버튼의 트리거는 상당히 부자연스럽다. 왜냐하면 커서가 밖에 있으면 파란색이고, 커서를 위로 가져가면 갑자기 빨간색으로 바뀌고 나오면 갑자기 파란색으로 변한다. 자연스럽게 서서히 변하게 만들고 싶다면, ColorAnimation을 이용하면 된다.

대강 생각해보면 다음과 같이 일어난다.

  1. 버튼에 마우스 커서가 들어온다.(MouseEnter Event)
  2. 동시에 애니메이션이 시작되고, 0.5초간 파란색이었던 배경이 붉은색으로 서서히 변한다. (ColorAnimation)
  3. 마우스커서가 버튼에서 나간다.(MouseLeave Event)
  4. 동시에 애니메이션이 시작되고, 0.5초간 붉은색이었던 배경이 푸른색으로 서서히 변한다.(ColorAnimation)

위의 시나리오대로 이벤트트리거를 만든다면, MouseEnter와 MouseLeave를 따로 만들어야한다.

[code language=”xml” title=”MouseEnter EventTrigger”]
<EventTrigger RoutedEvent="MouseEvent">
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
From="Blue" To="Red" Duration="0:0:0.5" />
</EventTrigger>
[/code]

[code language=”xml” title=”MouseLeave EventTrigger”]
<EventTrigger RoutedEvent="MouseLeave">
<ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
From="Red" To="Blue" Duration="0:0:0.5" />
</EventTrigger>
[/code]

TargetProperty가 조금 어렵게 생겼다.
Border의 Background의 SolidColorBrush의 Color값을 Blue에서 Red (혹은 Red에서 Blue)에서 바꾼다는 말이다. Duration은 해당 애니메이션이 일어나는 interval을 말한다.
BeginTime은 지정하지 않으면 해당 이벤트가 일어나자마자 실행되며, 설정을 하면 설정한 만큼 지연되어 일어난다.

위의 이벤트트리거 또한 ControlTemplate.Triggers 안에 넣으면 된다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다