안녕하세요, 이번 글에서는 Microsoft에서 제공하는 WPF [PasswordBox의 스타일 및 템플릿] 예제를 톺아보겠습니다.
PasswordBox
암호를 입력하고 처리하는 데 사용할 Control을 나타냅니다.
PasswordBox Style
예제의 XAML 코드를 PasswordBox에 적용하면 위와 같은 결과가 나옵니다. 이러한 결과에 도달하도록 코드 순서로 속성을 살펴보면 다음과 같습니다.
PasswordBox 속성
<Style x:Key="{x:Type PasswordBox}" TargetType="{x:Type PasswordBox}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="PasswordChar" Value="*" />
<Setter Property="MinWidth" Value="120" />
<Setter Property="MinHeight" Value="20" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="Template">
<Setter.Value>
...
</Setter.Value>
</Setter>
</Style>
x:Key
XAML 정의 사전에서 만들고 참고하는 요소를 고유하게 식별합니다. 문자를 이용하여 값을 설정하지만 예제에서처럼 x:Type을 사용하여 Control에 바로 적용하도록 설정할 수 있습니다.
TargetType
이 Style을 적용할 형식을 설정합니다.
SnapsToDevicePixels
렌더링 하는 동안 PasswordBox의 렌더링에 디바이스 관련 픽셀 스냅(Pixel Snap)을 사용할지 여부를 설정합니다. 픽셀 스냅을 사용하면 값을 true로 설정하고, 그렇지 않으면 false로 설정합니다.
⭐ 픽셀 스냅(Pixel Snap)을 사용하는 이유
WPF는 시스템 DPI 설정에 맞게 자동으로 크기를 조정하게 됩니다. 조정할 때 가장자리가 흐려지거나 반투명하게 표시되는 문제가 발생하는데, 이 문제를 해결하기 위해서 픽셀 스냅 기능을 사용하여 객체의 가장자리를 픽셀에 맞추어 고정시키는 기능을 제공합니다. 픽셀 스냅을 사용하면 객체에 작은 단위의 offset을 적용하여 객체의 크기를 픽셀에 맞추거나 일부분을 렌더링 시점에서 제거하여 해결하는 방법입니다.
OverridesDefaultStyle
테마 스타일의 스타일 속성을 포함할지 여부를 설정합니다. 이 요소가 테마 스타일 속성을 사용하지 않으면 true이고, 사용하면 false입니다.
KeyboardNavigation.TabNavigation
자식 요소에 대한 논리적 탭 탐색 동작을 설정합니다. 값으로는 Contained, Continue, Cycle, Local, None과 Once가 있습니다.
👀 값에 대한 자세한 설명은 이 페이지를 참고하시면 됩니다.
FocusVisualStyle
PasswordBox에 키보드 포커스가 있는 경우 PasswordBox에 적용되는 스타일을 설정합니다. 예제에서는 null 값으로 설정했습니다.
FontFamily
PasswordBox의 글꼴을 설정합니다.
PasswordChar
PasswordBox의 마스킹 문자를 설정합니다.
MinWidth
PasswordBox의 최소 너비를 설정합니다.
MinHeight
PasswordBox의 최소 높이를 설정합니다.
AllowDrop
PasswordBox를 끌어서 놓기 작업의 대상으로 사용할 수 있는지 여부를 설정합니다. 끌어서 놓기 작업의 대상으로 사용할 수 있으면 true이고, 그렇지 않으면 false입니다.
Template
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border x:Name="Border" ... >
...
<VisualStateManager.VisualStateGroups>
...
</VisualStateManager.VisualStateGroups>
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
PasswordBox의 템플릿을 설정합니다. 예제에서 사용되는 Template는 PasswordBox의 외형을 지정해줄 수 있는 ControlTemplate입니다.
👀 Template에 대해서 좀 더 아시고 싶으신 분은 이 페이지를 참고하시길 바랍니다.
PasswordBox Template 구성
PasswordBox는 Border가 ScrollViewer를 감싼 간단한 형태로 구성되어있습니다.
Border(Border)
다른 요소의 주위에 윤곽선, 배경 또는 둘 다를 그립니다.
⭐ Border 속성
<Border x:Name="Border" CornerRadius="2" Padding="2" BorderThickness="1">
<Border.Background>
<SolidColorBrush Color="{DynamicResource ControlLightColor}" />
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
...
</Border>
x:Name
Border에 이름을 설정합니다. 이름을 설정하는 것과 동시에 다른 곳에서 사용할 수 있도록 만들어줍니다.
CornerRadius
모서리의 둥근 정도를 설정합니다.
Padding
윤곽선 두께와 자식 요소 사이의 간격을 설정합니다.
BorderThickness
Border의 윤곽선 두께를 설정합니다.
Background
Border의 배경색을 설정합니다. 예제에서는 SolidColorBrush를 사용하여 ControlLightColor로 설정하였습니다.
BorderBrush
Border의 윤곽선 색을 설정합니다. 예제에서는 SolidColorBrush를 사용하여 BorderMediumColor로 설정하였습니다.
ScrollViewer(PART_ContentHost)
표시 가능한 다른 요소를 포함할 수 있는 Scroll 가능한 영역을 나타냅니다.
⭐ ScrollViewer 속성
<ScrollViewer x:Name="PART_ContentHost" />
x:Name
ScrollViewer에 이름을 설정합니다. 이름을 설정하는 것과 동시에 다른 곳에서 사용할 수 있도록 만들어줍니다. 예제에서는 "PART_ContentHost"로 설정하였습니다. Password에서 "PART_ContentHost"를 사용할 시 PasswordBox의 텍스트가 표시됩니다.
PasswordBox 이벤트
예제에서는 VisualStateManager를 사용하여 특정 변화를 주려고 한 것 같지만, 코드로는 아무런 변화가 없습니다. 혹시 이벤트를 주고 싶으실지도 모르는 분들을 위해서 예제를 한 개 추가했습니다. 예제의 경우 Disabled일 때 Border의 배경색이 변경되도록 코드를 작성하였습니다.
VisualStateManager
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="{StaticResource DisabledControlLightColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
VisualState는 Control의 시각적 상태를 나타냅니다. 예제의 이벤트에서 시각적 변화를 주기 위해서 VisualStateGroup의 x:Name을 CommonStates로 설정했습니다.
전체 코드
<Style x:Key="{x:Type PasswordBox}"
TargetType="{x:Type PasswordBox}">
<Setter Property="SnapsToDevicePixels"
Value="true" />
<Setter Property="OverridesDefaultStyle"
Value="true" />
<Setter Property="KeyboardNavigation.TabNavigation"
Value="None" />
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="FontFamily"
Value="Verdana" />
<Setter Property="PasswordChar"
Value="*" />
<Setter Property="MinWidth"
Value="120" />
<Setter Property="MinHeight"
Value="20" />
<Setter Property="AllowDrop"
Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border x:Name="Border"
CornerRadius="2"
Padding="2"
BorderThickness="1">
<Border.Background>
<SolidColorBrush Color="{DynamicResource ControlLightColor}" />
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BorderMediumColor}" />
</Border.BorderBrush>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled" />
<VisualState x:Name="MouseOver" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--Control colors.-->
<Color x:Key="WindowColor">#FFE8EDF9</Color>
<Color x:Key="ContentAreaColorLight">#FFC5CBF9</Color>
<Color x:Key="ContentAreaColorDark">#FF7381F9</Color>
<Color x:Key="DisabledControlLightColor">#FFE8EDF9</Color>
<Color x:Key="DisabledControlDarkColor">#FFC5CBF9</Color>
<Color x:Key="DisabledForegroundColor">#FF888888</Color>
<Color x:Key="SelectedBackgroundColor">#FFC5CBF9</Color>
<Color x:Key="SelectedUnfocusedColor">#FFDDDDDD</Color>
<Color x:Key="ControlLightColor">White</Color>
<Color x:Key="ControlMediumColor">#FF7381F9</Color>
<Color x:Key="ControlDarkColor">#FF211AA9</Color>
<Color x:Key="ControlMouseOverColor">#FF3843C4</Color>
<Color x:Key="ControlPressedColor">#FF211AA9</Color>
<Color x:Key="GlyphColor">#FF444444</Color>
<Color x:Key="GlyphMouseOver">sc#1, 0.004391443, 0.002428215, 0.242281124</Color>
<!--Border colors-->
<Color x:Key="BorderLightColor">#FFCCCCCC</Color>
<Color x:Key="BorderMediumColor">#FF888888</Color>
<Color x:Key="BorderDarkColor">#FF444444</Color>
<Color x:Key="PressedBorderLightColor">#FF888888</Color>
<Color x:Key="PressedBorderDarkColor">#FF444444</Color>
<Color x:Key="DisabledBorderLightColor">#FFAAAAAA</Color>
<Color x:Key="DisabledBorderDarkColor">#FF888888</Color>
<Color x:Key="DefaultBorderBrushDarkColor">Black</Color>
<!--Control-specific resources.-->
<Color x:Key="HeaderTopColor">#FFC5CBF9</Color>
<Color x:Key="DatagridCurrentCellBorderColor">Black</Color>
<Color x:Key="SliderTrackDarkColor">#FFC5CBF9</Color>
<Color x:Key="NavButtonFrameColor">#FF3843C4</Color>
<LinearGradientBrush x:Key="MenuPopupBrush"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="0.5" />
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#000000FF"
Offset="0" />
<GradientStop Color="#600000FF"
Offset="0.4" />
<GradientStop Color="#600000FF"
Offset="0.6" />
<GradientStop Color="#000000FF"
Offset="1" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
이 글의 내용은 아래의 사이트에서 기초합니다.
'프로그램 개발 > WPF: Style&Template' 카테고리의 다른 글
RadioButton (0) | 2022.06.16 |
---|---|
ProgressBar (0) | 2022.06.15 |
NavigationWindow③ (0) | 2022.06.14 |
NavigationWindow② (0) | 2022.06.13 |
NavigationWindow① (0) | 2022.06.11 |