XAML or HTML

022. 비헤이비어(CallMethodAction) #3

XAML 뽀개기

이전 포스트에서 살펴본 방법은 원하는 기능을 구현하는데 문제가 없었습니다. 하지만 기능을 필요로 하는 인터페이스를 마주 때마다 반복해야하는 비효율적인 문제가 예상되었습니다. 사실 문제를 해결하는 좋은 방법은 예 포스트들에서 이미 살펴보았습니다. 바로 Style(스타일), Template(템플릿) 관련 포스팅에서 말입니다.


관련 목차

 

018. 컨트롤템플릿(ControlTemplate) #1

019. 컨트롤템플릿(ControlTemplate) #2

 

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
<Window.Resources>
    <!-- ScrollViewer 기본 템플릿 -->
    <ControlTemplate x:Key="Template_ScrollViewer" TargetType="{x:Type ScrollViewer}">
        <Grid x:Name="Grid" Background="{TemplateBinding Background}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
 
            <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
            <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
            <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
            <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
 
            <!-- 바로가기 버튼 -->
            <Button x:Name="PART_ScrollToHome"
                    Content="↑" FontWeight="Bold" FontSize="18"
                    HorizontalAlignment="Right" VerticalAlignment="Bottom"
                    Margin="5">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <ei:CallMethodAction MethodName="ScrollToHome"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
        </Grid>
    </ControlTemplate>
</Window.Resources>
 
cs


ScrollViewer의 기본 템플릿을 구하는 방법은 예전 포스트 참고합니다. 조금 복잡해 보이지만 이번에 하고자 하는 기능 구현에만 집중합니다.

 

이전 포스트에서 ScrollViewer와 함께 Grid 묶여 있던 Button ScrollViewer의 기본 템플릿 안으로 옮기면 어떨까요? 만약 그렇게 한다면 템플릿은 리소스로 정의하기 때문에 해당 템플릿 리소스를 사용하는 ScrollViewer라면 해당 요소 기능을 자동으로 가지게 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
<Button x:Name="PART_ScrollToHome"
        Content="↑" FontWeight="Bold" FontSize="18"
        HorizontalAlignment="Right" VerticalAlignment="Bottom"
        Margin="5">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <ei:CallMethodAction TargetObject="{Binding  RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" 
                                 MethodName="ScrollToHome"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
cs

 

여기서 주의할 점은 Button의 위치가 ScrollViewer 컨트롤의 밖에서 ScrollViewer의 템플릿 안으로 변경되었기 때문에 TargetObject 속성을 제대로 찾을 없게 된다는 점입니다.

 

1
TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}"
cs

 

첫번째AncestorType(원형이 되는 타입) 지정해 찾는 방법으로 간단히 바꿔 있습니다.

 

1
TargetObject="{Binding RelativeSource={RelativeSource TemplatedParent}}"
cs

 

두번째템플릿 부모를 찾는 방법도 있습니다. 예전 포스트에서도 다룬 적이 있으니 참고하세요.

 

1
2
3
4
5
6
7
8
9
10
11
<!-- ScrollViewer 템플릿 -->
<ScrollViewer Grid.Column="1" 
              Template="{DynamicResource Template_ScrollViewer}">
    <StackPanel Margin="5">
        <TextBlock Text="A"/>
                
        .....
 
        <TextBlock Text="Z"/>
    </StackPanel>
</ScrollViewer>
cs

 

 

앞에서 완료한 ScrollViewer의 템플릿을 ScrollViewer에 적용한 태스트해봅니다.

 

그런데 목록형 컨텐츠를 표현할 ScrollViewer만 단독으로 사용하는 경우는 드뭅니다. ItemsSource 속성, DataTemplate, 아이템을 선택했을 때 상태변경  여러 이유로 ListBox 자주 사용합니다. ListView, DataGrid 컨트롤 등도 마찬가지 입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- ListBox 기본 스타일 -->
<Style x:Key="Style_ListBox" TargetType="{x:Type ListBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
                    <ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}" 
                                  Template="{DynamicResource Template_ScrollViewer}">
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </ScrollViewer>
                </Border>                    
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
 
cs

 

ListBox 기본 템플릿을 예전 포스트 참고해 스타일과 함께 리소스에 정의하고 이번에 살펴보고 있는 기능 구현에만 집중하기 위해 다른 Setter Trigger 정의들은 제거했습니다.


ListBox 기본 템플릿은 ScrollViewer를 포함하고 습니다. 이전과 동일한 방법으로 ScrollViewer 템플릿을 ScrollViewer의 템플릿으로 정의합니다.

 

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
<!-- ListBox 스타일-->
<ListBox Grid.Column="2" 
         Style="{DynamicResource Style_ListBox}">
    <sys:Char>A</sys:Char>
    <sys:Char>B</sys:Char>
    <sys:Char>C</sys:Char>
    <sys:Char>D</sys:Char>
    <sys:Char>E</sys:Char>
    <sys:Char>F</sys:Char>
    <sys:Char>G</sys:Char>
    <sys:Char>H</sys:Char>
    <sys:Char>I</sys:Char>
    <sys:Char>J</sys:Char>
    <sys:Char>K</sys:Char>
    <sys:Char>L</sys:Char>
    <sys:Char>M</sys:Char>
    <sys:Char>N</sys:Char>
    <sys:Char>O</sys:Char>
    <sys:Char>P</sys:Char>
    <sys:Char>Q</sys:Char>
    <sys:Char>R</sys:Char>
    <sys:Char>S</sys:Char>
    <sys:Char>T</sys:Char>
    <sys:Char>U</sys:Char>
    <sys:Char>V</sys:Char>
    <sys:Char>W</sys:Char>
    <sys:Char>X</sys:Char>
    <sys:Char>Y</sys:Char>
    <sys:Char>Z</sys:Char>
</ListBox>
cs

 

그런 다음 ListBox의 스타일에 정의만해주면 끝입니다

 


실행해서 태스트해봅니다. 더 많은 기본 비헤이비어에 대해서는 아래 MSDN 링크를 따라 참고하세요.


샘플 코드 : https://github.com/CharlesKwon/XamlSimplified


참고 : MSDN > Working with built-in behaviors


관련 목차

 

020. 비헤이비어(Behavior) #1

021. 비헤이비어(CallMethodAction) #2

022. 비헤이비어(CallMethodAction) #3