XAML or HTML

027. 툴팁 말풍선 슬라이더(ToolTip Balloon Slider) #1

XAML 뽀개기

이전 포스트에서도 잠깐 설명했지만 Slider Thumb() 드래그하면 현재 Value() 툴팁 말풍선 안에서 표출되는 간단한 기능을 구현해보려 합니다. 여러가지 방식으로 접근해보면서 마주하게 되는 문제점들을 하나씩 짚어봅시다.

 

 

툴팁 말풍선이 Slider 썸을 따라다니도록 하고 싶습니다. 먼저 썸이 어디에 있는지 찾아 봅시다.

 


Blend for VS [템플릿 편집 > 복사본 편집기능을 이용해 Slider 기본 스타일 템플릿을 얻을 있습니다.

 

1
2
3
<!-- ToolTip of Thumb -->
<Slider Value="5" VerticalAlignment="Center" Margin="10,0" 
        Style="{DynamicResource Style_Slider}"/>
cs

 

자동으로 지정한 키네임이 스타일 속성에 선언됩니다.

 

 

[템플릿 편집 > 복사본 편집] 기능을 이용해 얻어낸 Slider 기본 스타일 템플릿의 양이 많아 혼란이 있습니다. 조금만 정리해서 보면 그렇게 복잡하지 않습니다.

 

 

비슷한 역할을 하는 것들 끼리 묶어서 정리한 주석을 약간 추가해주었습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Slider 기본 스타일 -->
<Style x:Key="Style_Slider" TargetType="{x:Type Slider}">
    <Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="Foreground" Value="{StaticResource SliderThumb.Static.Foreground}"/>
    <Setter Property="Template" Value="{StaticResource SliderHorizontal}"/>
    <Style.Triggers>
        <Trigger Property="Orientation" Value="Vertical">
            <Setter Property="Template" Value="{StaticResource SliderVertical}"/>
        </Trigger>
    </Style.Triggers>
</Style>
cs

 

가장 먼저 Slider의 기본 스타일을 살펴보면 기본 템플릿이 SliderHorizontal이고 Orientation 속성이 Vertical (Trigger) SliderVertical 템플릿으로 변경된다는 것을 있습니다.

 

여러 템플릿들의 키네임을 보면 역활들이 어느정도 추측 가능합니다. 단지 슬라이더의 가로, 세로 방향에 따라 가로형, 세로형 템플릿들이 각각 준비되어 있는 뿐입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- Slider 가로형 템플릿 -->
<ControlTemplate x:Key="SliderHorizontal" TargetType="{x:Type Slider}">
    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TickBar x:Name="TopTick" Fill="{TemplateBinding Foreground}" Height="4" Margin="0,0,0,2" Placement="Top" Grid.Row="0" Visibility="Collapsed"/>
            <TickBar x:Name="BottomTick" Fill="{TemplateBinding Foreground}" Height="4" Margin="0,2,0,0" Placement="Bottom" Grid.Row="2" Visibility="Collapsed"/>
            <Border x:Name="TrackBackground" BorderBrush="{StaticResource SliderThumb.Track.Border}" BorderThickness="1" Background="{StaticResource SliderThumb.Track.Background}" Height="4.0" Margin="5,0" Grid.Row="1" VerticalAlignment="center">
                <Canvas Margin="-6,-1">
                    <Rectangle x:Name="PART_SelectionRange" Fill="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" Height="4.0" Visibility="Hidden"/>
                </Canvas>
            </Border>
            <Track x:Name="PART_Track" Grid.Row="1">
                <Track.DecreaseRepeatButton>
                    <RepeatButton Command="{x:Static Slider.DecreaseLarge}" Style="{StaticResource RepeatButtonTransparent}"/>
                </Track.DecreaseRepeatButton>
                <Track.IncreaseRepeatButton>
                    <RepeatButton Command="{x:Static Slider.IncreaseLarge}" Style="{StaticResource RepeatButtonTransparent}"/>
                </Track.IncreaseRepeatButton>
                <Track.Thumb>
                    <Thumb x:Name="Thumb" Focusable="False" Height="18" OverridesDefaultStyle="True" Template="{StaticResource SliderThumbHorizontalDefault}" VerticalAlignment="Center" Width="11"/>
                </Track.Thumb>
            </Track>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
 
    // 생략
 
    </ControlTemplate.Triggers>
</ControlTemplate>
 
cs

 

역으로 거슬러 올라가 기본 템플릿으로 정의되어 있는 SliderHorizontal 템플릿을 살펴보겠습니다. 여기서 썸을 찾을 있습니다. PART_Track이라는 이름의 요소 하위에 있는 것을 발견했습니다.

 

1
2
3
<Track.Thumb>
    <Thumb x:Name="Thumb" Focusable="False" Height="18" OverridesDefaultStyle="True" Template="{StaticResource SliderThumbHorizontalDefault}" VerticalAlignment="Center" Width="11"/>
</Track.Thumb>
cs

 

부분이 썸의 역활을 하는 XAML 코드입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- ToolTip 스타일 & 템플릿 -->
<ControlTemplate x:Key="Template_ToolTip" TargetType="{x:Type ToolTip}">
    <StackPanel>
        <Border CornerRadius="5" Background="SkyBlue">
            <ContentPresenter HorizontalAlignment="Center" Margin="10,5"/>
        </Border>
        <ed:RegularPolygon Fill="SkyBlue" Margin="0,-8,0,0" Panel.ZIndex="-1" 
                           PointCount="4" Width="10" Height="15"/>
    </StackPanel>
</ControlTemplate>
<Style x:Key="Style_ToolTip" TargetType="{x:Type ToolTip}">
    <Setter Property="Template" Value="{StaticResource Template_ToolTip}"/>
</Style>
cs

 

리소스에 툴팁 말풍선의 스타일과 템플릿을 추가로 선언합니다. 이전 포스트에서 만들었던 말풍선 팝업 XAML 코드 첫번째 방법의 코드를 그대로 가지고 왔습니다. 약간 수정이 필요합니다. 기존에 TextBlock ContentPresenter 대체합니다. 예전 포스트들에서 ContentPresenter 암시적으로 바인딩되어 진다고 여러 설명했습니다. 여기서 Style_ToolTip과 Template_ToolTip으로 나누어 정의한 것에 의미는 현재 없습니다.

 

1
2
3
4
5
6
7
8
9
10
11
<Track.Thumb>
    <Thumb x:Name="Thumb" Focusable="False" Height="18" OverridesDefaultStyle="True" Template="{StaticResource SliderThumbHorizontalDefault}" VerticalAlignment="Center" Width="11">
        <Thumb.ToolTip>
            <ToolTip x:Name="PART_ThumbToolTip" 
                     Template="{StaticResource Template_ToolTip}"
                     Content="{TemplateBinding Value}" 
                     Placement="Center" VerticalOffset="-30"
                     ContentStringFormat="N0"/>
        </Thumb.ToolTip>
    </Thumb>
</Track.Thumb>
cs

 

이렇게 만든 말풍선 툴팁 템플릿을 썸의 툴팁 템플릿으로 지정합니다. 그리고 툴팁의 Content 속성은 TemplateBinding 문법을 이용해 Value 바인딩합니다. 그런 Placement, VerticalOffset 속성을 이용해 원하는 위치에 말풍선 툴팁이 위치하도록 합니다.

 

 

불필요한 소숫점 자리의 값을 없애기 위해 ContentStringFormat 속성에 "N0" 선언합니다.

 

실행해서 태스트해보면 썸에 마우스를 올려놓았을 툴팁 말풍선이 노출됩니다. 하지만 드래그하는 순간 사라지고 맙니다. 이것은 제가 원하는 시나리오가 아닙니다. 드래그하는 도중에는 반드시 노출되었으면 합니다. 썸에 직접 툴팁을 선언하는 것은 옳은 방법이 아닌 같습니다.

 

1
2
3
<!-- Auto ToolTip : Only mouse-->
<Slider Value="5" VerticalAlignment="Center" Margin="10,0" Grid.Row="1" 
        AutoToolTipPlacement="TopLeft"/>
cs

 

두번째 Slider 새로히 만들어 다른 방식으로 접근해보겠습니다.

 

 

Slider AutoToolTipPlacement 속성을 정의하면 마우스 드래그를 하는 동안 툴팁이 자동으로 노출됩니다. 매우 간단하며 제가 원했던 시나리오이기도 합니다.

 

1
2
3
4
5
6
7
8
9
<!-- Auto ToolTip : Only mouse-->
<Slider Value="5" VerticalAlignment="Center" Margin="10,0" Grid.Row="1" 
        AutoToolTipPlacement="TopLeft">
    <Slider.Resources>
        <Style TargetType="{x:Type ToolTip}">
            <Setter Property="Template" Value="{StaticResource Template_ToolTip}"/>
        </Style>
    </Slider.Resources>
</Slider>
cs

 

툴팁 말풍선의 템플릿은 Slider 하위에 키네임을 생략한 리소스를 정의하는 방식으로 적용할 있습니다.

 

1
2
3
4
5
6
7
<!-- Auto ToolTip : Only mouse-->
<Slider Value="5" VerticalAlignment="Center" Margin="10,0" Grid.Row="1" 
        AutoToolTipPlacement="TopLeft">
    <Slider.Resources>
        <Style TargetType="{x:Type ToolTip}" BasedOn="{StaticResource Style_ToolTip}"/>            
    </Slider.Resources>
</Slider>
cs

 

아니면 스타일의 BasedOn 속성을 이용해 Style_ToolTip을 정의할 수도 있습니다. 결과적으로는 차이가 없지만 방법에는 차이가 있습니다. Style_ToolTip과 Template_ToolTip으로 나누어 정의한 것의 의도하는 바가 여기에 있습니다.

 

 

하지만 방법도 한계가 있습니다. 일반적으로 기본 슬라이더는 마우스를 사용하지 않고 키보드만으로도 사용할 있습니다. 슬라이더에 포커스가 있는 상태에서 키보드의 방향키를 누르는 방법으로도 값을 변경할 있습니다. 하지만 툴팁 말풍선이 노출되지 않는 문제를 발견할 있습니다.

 

다음 포스트에서는 마우스 키보드 모두 툴팁 말풍선이 노출되도록 하는 방법에 대해 알아보겠습니다.