XAML or HTML

014. 결합 속성(Attached property)

XAML 뽀개기

AttachedProperty.cs : 의존 속성

 

샘플 프로젝트에 AttachedProperty.cs 파일을 추가하고 SetFontPanel 클래스를 작성합니다.


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
#region ChildrenFontSize
 
public static readonly DependencyProperty ChildrenFontSizeProperty =
    DependencyProperty.RegisterAttached(
        "ChildrenFontSize",
        typeof(double),
        typeof(SetFontPanel),
        new PropertyMetadata((double)12, OnChildrenFontSizePropertyChanged));
 
public static double GetChildrenFontSize(DependencyObject dp)
{
    return (double)dp.GetValue(ChildrenFontSizeProperty);
}
 
public static void SetChildrenFontSize(DependencyObject dp, double value)
{
    dp.SetValue(ChildrenFontSizeProperty, value);
}
 
private static void OnChildrenFontSizePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    var panel = sender as Panel;
    if (panel == nullreturn;
 
    SetFontProperties(panel);
}
 
#endregion
 
cs


ChildrenFontSize라는 Attached Property 작성했습니다. Attached property Dependency property입니다. Dependency property Register 함수 정의하고 Attached property RegisterAttached 함수 정의하는 차이가 있습니다. Get Set 함수를 갖고 있는 점도 특징입니다. 자세한 내용은 박문찬 MVP님의 블로그를 참고하세요.

 

추가로 PropertyChanged 이벤트가 발생할 때마다 SetFontProperties 함수가 호출되도록 했습니다.

 

#region ChildrenForeground

 

.....

 

#endregion

 

ChildrenForeground라는 결합 속성도 추가했지만 반복되는 내용이므로 설명은 생략합니다.

FontFamily, FontWeight, FontStyle, FontStretch 사용하고자하는 결합 속성들이 추가될 있습니다.

 

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
#region Attach
 
public static readonly DependencyProperty AttachProperty =
    DependencyProperty.RegisterAttached(
        "Attach",
        typeof(bool),
        typeof(SetFontPanel),
        new PropertyMetadata(false, OnAttachPropertyChanged));
 
public static bool GetAttach(DependencyObject dp)
{
    return (bool)dp.GetValue(AttachProperty);
}
 
public static void SetAttach(DependencyObject dp, bool value)
{
    dp.SetValue(AttachProperty, value);
}
 
private static void OnAttachPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    var panel = sender as Panel;
    if (panel == nullreturn;
 
    if ((bool)e.NewValue)
    {
        panel.Loaded += panel_Loaded;
    }
    else
    {
        panel.Loaded -= panel_Loaded;
    }
}
 
#endregion
 
cs


Loaded 이벤트일 한번만 SetFontProperties 함수를 호출하기 위해서 Attach라는 bool형의 결합 속성도 추가했습니다.

 

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
37
38
39
40
#region Method
 
private static void SetFontProperties(Panel panel)
{
    foreach (var child in panel.Children)
    {
        if (child == nullreturn;
 
        if (child is TextBlock)
        {
            var txt = child as TextBlock;
            if (txt == nullreturn;
 
            txt.FontSize = GetChildrenFontSize(panel);
            txt.Foreground = GetChildrenForeground(panel);
            // FontFamily
            // FontWeight
            // FontStyle
            // FontStretch
        }
        else if (child is Control)
        {
            var ctr = child as Control;
            if (ctr == nullreturn;
 
            ctr.FontSize = GetChildrenFontSize(panel);
            ctr.Foreground = GetChildrenForeground(panel);
            // FontFamily
            // FontWeight
            // FontStyle
            // FontStretch
        }
        //else if (또 다른 컨트롤을 추가)
        //{
 
        //}
    }
}
 
#endregion
cs

 

Panel 안에 들어간 컨트롤들을 모두 찾아 앞에서 추가한 결합 속성들을 알맞은 속성에 대입해주는 함수를 작성했습니다. 파라미터를 Panel 지정해 WrapPanel, UniformGrid Panel 상속 받은 레이아웃 패널이라면 범용적으로 사용 가능하도록 했습니다.

 

1
xmlns:atcp="clr-namespace:Sample.AttachedProperty"
cs

 

앞에서 작성한 결합 속성을 XAML에서 사용하기 위해 네임스페이스를 선언합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Attached property -->
<StackPanel x:Name="spParent" 
            HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"
            atcp:SetFontPanel.ChildrenFontSize="15" 
            atcp:SetFontPanel.ChildrenForeground="Red" 
            atcp:SetFontPanel.Attach="True">
            <!-- [Prefix].[ClassName].[AttachedProperty] -->
    <Label Content="Attached property"/>
    <TextBlock Text="Lorem Ipsum is simply "/>
    <TextBlock Text="dummy text of the"/>
    <TextBlock Text="printing and typesetting "/>
    <TextBlock Text="industry."/>
</StackPanel>
cs

 

결합 속성을 사용하는 방법은 위와 같습니다. "[접두사].[클래스명].[결합속성명]"


 

부모가 자식들을 일괄적으로 Font 관련 속성들을 정의할 있게 되었습니다. 결합 속성이 일반 속성과 다른 점은 직접 사용하는 속성이 아니라 다른 요소가 사용하도록 하는 점입니다. 이 예제에서는 자식들이 그 결합 속성을 사용하도록 했습니다. 


이 예제에서 추가한 결합 속성은 사용자가 정의한 것 입니다. 사실 우리는 이미 많은 결합 속성을 사용하고 있습니다. 예를 들어 보겠습니다. 

1
2
3
4
5
6
7
8
9
10
11
12
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
 
    <TextBlock Grid.Column="1"/>
</Grid>
 
<Canvas>
    <TextBlock Canvas.Left="100" Canvas.Top="50"/>
</Canvas>
cs

부모 Grid의 자식인 TextBlock에 선언된 Grid.Column 속성과 부모 Canvas의 자식인 TextBlock에 선언된 Canvas.Left 속성이 바로 결합속성입니다. 자신에게 없는 속성이지만 각 패널 안에서 위치를 정의하는데 사용되고 있습니다.


이처럼 결합속성은 자신이 가지고 있지 않은 속성을 정의해 다양한 방식으로 응용될 수 있습니다. 위 예제에서는 부모가 자식의 위치를 직접 정의해줄 수 없기 떄문에 자식 스스로가 부모의 결합속성을 이용해 자신의 위치를 정의하고 있는 것입니다.


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