WPF에서 사용되는 ComboBox는 Background 속성이 설정되지 않습니다.
그래서 ComboBox를 디자인하는 방법을 알기 위해서는 Style, ControlTemplate의 적용이 필수라는 것을 알게 되었습니다.
Style, ControlTempate에 대한 사용법과 이 2가지를 조합하여 ComboBox를 만드는 법을 설명하겠습니다.
Style의 사용
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<!-- Window 내부에서 리소스를 사용하기 위해 --!>
<Window.Resources>
<!-- Key는 외부에서 사용하기 위한 이름, TargetType은 컨트롤 유형 --!>
<Style x:Key="StdCmbBox" TargetType="{x:Type ComboBox}">
<!-- Setter를 이용하여 Property, Value를 지정할 수 있다 --!>
<Setter Property="MinHeight" Value="20"></Setter>
<Setter Property="MinWidth" Value="80"></Setter>
<Setter Property="Width" Value="100"></Setter>
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Foreground" Value="DarkOrange"></Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!-- normalCmbBox에 Style을 적용하기 위해서는--!>
<!-- "{StaticResource StdCmbBox}"로 등록하면 된다--!>
<ComboBox x:Name="normalCmbBox" Style="{StaticResource StdCmbBox}">
<ComboBoxItem Content="test1"></ComboBoxItem>
<ComboBoxItem Content="test2"></ComboBoxItem>
</ComboBox>
</Grid>
</Window>
ControlTemplate의 사용
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<Window.Resources>
<!-- ToggleButton 타입을 ComboBoxToggleButton 키로 접근하면 Template을 쓸수있다. --!>
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border" Grid.ColumnSpan="2" CornerRadius="0"
Background="#FF323232" BorderBrush="#FF97A0A5" BorderThickness="1" />
<Border
Grid.Column="0" CornerRadius="0" Margin="1"
Background="#FF323232" BorderBrush="#FF4b4b4b" BorderThickness="0,0,2,0" />
<!-- 아래 방향의 삼각형을 만드는 방법 -->
<!-- M (0,0) 절대경로에서 (4,4)로 Line, (8,0)로 Line로 간 다음 Z(종료)
<Path
x:Name="Arrow" Grid.Column="1" Fill="DarkOrange"
HorizontalAlignment="Center" VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"
/>
</Grid>
</ControlTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ToggleButton Grid.Column="0" Margin="10" Template="{StaticResource ComboBoxToggleButton}"></ToggleButton>
</Grid>
</Window>
✔️ Path 정보를 더 참고하고 싶을 때!
Style, ControlTemplate을 조합한 ComboBox 만들기
우선 <Window.Resource> 내부에 Style, ControlTemplate을 다 적용하게 되면 Xaml의 길이가 매우 길어지게 된다.
그래서 ResourceDictionary를 App.xaml 등록하여 사용합니다.
App.xaml
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CmbBoxStyle.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
CmbBoxStyle.xaml
Style 설계
- 콤보박스를 [텍스트박스 + 토글버튼] 으로 구성
- 토글버튼을 누르면 아래 방향으로 Slide 하여 Popup 오픈
- Popup이 열리면 콤보박스 항목이 보이며 항목을 선택하면 텍스트 박스에 표시됨
- 콤보박스 항목도 ControlTemplate 적용
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- ToggleButton의 Template 생성 --!>
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan="2"
CornerRadius="0"
Background="#FF323232"
BorderBrush="#FF97A0A5"
BorderThickness="1" />
<Border
Grid.Column="0"
CornerRadius="0"
Margin="1"
Background="#FF323232"
BorderBrush="#FF4b4b4b"
BorderThickness="0,0,2,0" />
<Path
x:Name="Arrow"
Grid.Column="1"
Fill="DarkOrange"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"
/>
</Grid>
</ControlTemplate>
<!-- TextBox의 Template 생성 --!>
<!-- 부모 컨트롤의 속성을 받아들이기 위해 TemplateBinding 사용 --!>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
<Border x:Name="PART_ContentHost" Focusable="False"
Background="{TemplateBinding Background}" />
</ControlTemplate>
<!-- ComboBox의 Style을 Key로 접근할 수 있게 만듬 --!>
<Style x:Key="StdCmbBox" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="80"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Foreground" Value="DarkOrange"/>
<!-- Style 내부에서 외부의 Template을 가져올 수 있음 --!>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!-- IsChecked 속성에 사용된 "{Binding}" --!>
<!-- IsDropDownOpen 속성이 true면 ToggleButton IsChecked도 true --!>
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter Name="ContentSite" IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Focusable="True"
Background="#FF323232"
Foreground="DarkOrange"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
x:Name="DropDownBorder"
Background="#FF323232"
BorderThickness="1"
BorderBrush="#888888"/>
<ScrollViewer Margin="2,4,2,4" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<!-- ControlTemplate 동작에 관련 내용이다. --!>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="0"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
<!-- ComboBoxItem의 스타일 생성 --!>
<Style x:Key="StdCmbBoxItem" TargetType="{x:Type ComboBoxItem}">
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Foreground" Value="DarkOrange"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border Name="Border"
Padding="1" BorderBrush="Black" BorderThickness="1"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="#FF4b4b4b"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="LightGray"/>
<Setter Property="Background" Value="DarkGray"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
💥Xaml에서 사용한 속성 정리
SnapToDevicePixels | Offset을 적용하여 객체의 크기를 장치 픽셀에 맞추거나 렌더링 시점에서 제거 |
OverrideDefaultStyle | 테마 스타일 속성을 포함할 지 여부, 기본 컨트롤 템플릿 사용X |
ScrollViewer.CanContentScroll | 실제 스크롤(실제 픽셀만큼)을 해야하는 경우 false, 아니면 true |
Focusable | 요소가 Focus를 받을 수 있는지 여부 |
IsHitTestVisible | False로 설정하면 보이지 않는 상태로 만들 수 있다 (기본값은 True) |
ContentPresenter | 외부의 Content 속성이 ContentPresenter가 만들어진 곳에 사용된다. |
AllowTransparency | 창에서 투명도를 지원하면 true, 아니면 false, 배경을 투명으로 하면 설정 true |
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ComboBox x:Name="cmbBox1" Margin="20" Style="{StaticResource StdCmbBox}">
<ComboBoxItem Content="test1" Style="{StaticResource StdCmbBoxItem}"></ComboBoxItem>
<ComboBoxItem Content="test2" Style="{StaticResource StdCmbBoxItem}"></ComboBoxItem>
<ComboBoxItem Content="test3" Style="{StaticResource StdCmbBoxItem}"></ComboBoxItem>
</ComboBox>
</Grid>
</Window>
각종 커스텀 컨트롤을 만들고 싶으면 Style, ControlTemplate을 많이 사용해 보는 것이 좋습니다.
저도 프로젝트를 진행할 때마다 여러개 만들어보는 편입니다!.
감사합니다.
'WPF' 카테고리의 다른 글
[WPF] - INotifyPropertyChanged를 이용한 Binding 방법 (0) | 2023.01.01 |
---|---|
[WPF] - Border (0) | 2022.12.31 |
[WPF] - ResourceDictionary 사용 (0) | 2022.12.30 |
[WPF] - 프로젝트 생성 및 시작 (0) | 2022.12.28 |
[WPF] - 시작 (0) | 2022.12.28 |