다음을 통해 공유


사용자 지정 연결된 속성

결합속성은 XAML로 정의된 개념입니다. 연결된 속성은 일반적으로 특수한 형태의 종속성 속성으로 정의됩니다. 이 항목에서는 연결된 속성을 종속성 속성으로 구현하는 방법과 연결된 속성을 XAML에서 사용할 수 있도록 하는 데 필요한 접근자 규칙을 정의하는 방법에 대해 설명합니다.

필수 조건

이 항목에서는 기존 종속성 속성의 소비자 관점에서 종속성 속성을 이해하고 종속성 속성 개요 항목을 읽었다고 가정합니다. 연결된 속성 개요도 읽어야 합니다. 이 항목의 예제를 따르려면 XAML을 이해하고 C++, C# 또는 Visual Basic을 사용하여 기본 Windows 런타임 앱을 작성하는 방법도 알아야 합니다.

연결된 속성에 대한 시나리오

클래스 정의 외에 클래스에 사용할 수 있는 메커니즘을 설정하는 이유가 있는 경우 연결된 속성을 만들 수 있습니다. 이에 대한 가장 일반적인 시나리오는 레이아웃 및 서비스 지원입니다. 기존 레이아웃 속성 의 예로 Canvas.ZIndexCanvas.Top 있습니다. 레이아웃 시나리오에서는 레이아웃 제어 요소에 대한 자식 요소로 존재하는 요소가 개별적으로 해당 레이아웃 부모 요소에 대한 레이아웃 요구사항을 표현할 수 있으며, 각각은 부모가 연결된 속성으로 정의한 속성 값을 설정합니다. Windows 런타임 API의 서비스 지원 시나리오의 예는 ScrollViewer.IsZoomChainingEnabled와 같은 ScrollViewer연결된 속성 집합입니다.

Warning

Windows 런타임 XAML 구현의 기존 제한 사항은 사용자 지정 연결된 속성을 애니메이션할 수 없다는 점입니다.

사용자 지정 연결된 속성 등록

사용자가 다른 유형에 대해 사용하도록 연결된 속성을 엄격하게 정의하는 경우, 속성이 등록된 클래스는 DependencyObject에서 파생될 필요는 없습니다. 그러나 연결된 속성도 종속성 속성으로 사용하는 일반적인 모델을 따르는 경우 지원 속성 저장소를 사용할 수 있도록 접근자에 대한 대상 매개 변수가 DependencyObject 를 사용해야 합니다.

DependencyProperty 형식의 공용 정적 읽기 전용 속성을 선언하여 연결된 속성을 종속성 속성으로 정의합니다. RegisterAttached메서드의 반환 값을 사용하여 이 필드를 정의합니다. 속성 이름은 RegisterAttached 이름 매개 변수로 지정한 연결된 속성 이름과 일치해야 하며 문자열 "Property"는 끝에 추가됩니다. 이는 종속성 속성이 나타내는 속성과 관련하여 종속성 속성의 식별자 이름을 지정하기 위한 설정된 규칙입니다.

사용자 지정 연결된 속성을 정의하는 것이 사용자 지정 종속성 속성과 다른 기본 영역은 접근자 또는 래퍼를 정의하는 방법입니다. 사용자 지정 종속성 속성에 설명된 래퍼 기술을 사용하는 대신 정적 GetPropertyName 및 SetPropertyName 메서드를 연결된 속성의 접근자로 제공해야 합니다. 접근자는 주로 XAML 파서에서 사용되지만 다른 호출자는 이 접근자를 사용하여 XAML이 아닌 시나리오에서 값을 설정할 수도 있습니다.

Important

접근자를 올바르게 정의하지 않으면 XAML 프로세서가 연결된 속성에 액세스할 수 없고 연결된 속성을 사용하려는 사용자에게 XAML 파서 오류가 발생할 수 있습니다. 디자인 및 코딩 도구도 참조된 어셈블리에서 사용자 지정 종속성 속성이 발견되는 경우 "*Property" 규칙에 따라 식별자를 명명하는 경우가 많습니다.

접근자

GetPropertyName 접근자의 서명다음과 같아야 합니다.

public staticvalueType PropertyName 가져오기 (DependencyObject target)

Microsoft Visual Basic의 경우 다음과 같습니다.

Public Shared Function GetPropertyName(ByVal target As DependencyObject) As valueType)

대상 개체는 구현에서 보다 구체적인 형식일 수 있지만 DependencyObject에서 파생되어야 합니다. 구현에서 보다 구체적인 형식으로 반환 값을 지정할 수 있습니다. 기본 개체 형식은 허용 가능하지만 연결된 속성이 형식 보안을 적용하는 경우가 많습니다. getter 및 setter 서명에 입력을 사용하는 것이 권장되는 형식 안전 기술입니다.

SetPropertyName 접근자의 서명은 다음과 같아야 합니다.

public static void SetPropertyName(DependencyObject target ,valueType value)

Visual Basic의 경우 다음과 같습니다.

Public Shared Sub SetPropertyName(ByVal target As DependencyObject, ByVal value AsvalueType)

대상 개체는 구현에서 보다 구체적인 형식일 수 있지만 DependencyObject에서 파생되어야 합니다. 개체와 해당 valueType구현에서 보다 구체적인 형식일 수 있습니다. 태그의 연결된 속성 사용에서 연결된 속성에 있는 경우, 이 메서드의 값은 XAML 프로세서에서 오는 입력값 임을 기억하십시오. 따라서 형식 변환, 또는 사용하는 형식에 대한 태그 확장 지원이 있어야, 적절한 형식이 특성 값(궁극적으로 문자열만)에서 생성될 수 있습니다. 기본 개체 형식은 허용 가능하지만 추가 형식 안전이 필요합니다. 이를 위해 접근자에 형식 적용을 배치합니다.

참고 항목

속성 요소 구문을 통해 사용하려는 연결된 속성을 정의할 수도 있습니다. 이 경우 값에 대한 형식 변환이 필요하지 않지만 XAML에서 원하는 값을 생성할 수 있는지 확인해야 합니다. VisualStateManager.VisualStateGroups는 속성 요소 사용만 지원하는 기존 연결된 속성의 예입니다.

코드 예

이 예제에서는 사용자 지정 연결된 속성에 대한 종속성 속성 등록(RegisterAttached 메서드 사용)과 GetSet 접근자를 보여 줍니다. 예제에서 연결된 속성 이름은 IsMovable입니다. 따라서 접근자의 이름은 GetIsMovableSetIsMovable입니다. 연결된 속성의 소유자는 자체 UI가 없는 서비스 GameService 클래스입니다. 그 목적은 GameService.IsMovable 연결된 속성이 사용되는 경우에만 연결된 속성 서비스를 제공하는 것입니다.

C++/CX에서 연결된 속성을 정의하는 것은 약간 더 복잡합니다. 헤더 파일과 코드 파일 간에 요소를 지정하는 방법을 결정해야 합니다. 또한 사용자 지정 종속성 속성에 설명된 이유로 GET 접근자만 있는 속성으로 식별자를 노출해야 합니다. C++/CX에서는 단순 속성에 대한 .NET readonly 키워딩 및 암시적 지원에 의존하지 말고 명시적으로 이러한 속성-필드 관계를 정의해야 합니다. 또한 앱이 처음 시작될 때와 연결된 속성이 필요한 XAML 페이지가 로드 되기 전에 한 번만 실행되는 도우미 함수 내에서 연결된 속성의 등록을 수행해야 합니다. 모든 종속성 또는 연결된 속성에 대해 속성 등록 도우미 함수를 호출하는 일반적인 위치는 app.xaml 파일에 대한 코드의 앱 / 애플리케이션 생성자 내입니다.

public class GameService : DependencyObject
{
    public static readonly DependencyProperty IsMovableProperty = 
    DependencyProperty.RegisterAttached(
      "IsMovable",
      typeof(Boolean),
      typeof(GameService),
      new PropertyMetadata(false)
    );
    public static void SetIsMovable(UIElement element, Boolean value)
    {
        element.SetValue(IsMovableProperty, value);
    }
    public static Boolean GetIsMovable(UIElement element)
    {
        return (Boolean)element.GetValue(IsMovableProperty);
    }
}
Public Class GameService
    Inherits DependencyObject

    Public Shared ReadOnly IsMovableProperty As DependencyProperty = 
        DependencyProperty.RegisterAttached("IsMovable",  
        GetType(Boolean), 
        GetType(GameService), 
        New PropertyMetadata(False))

    Public Shared Sub SetIsMovable(ByRef element As UIElement, value As Boolean)
        element.SetValue(IsMovableProperty, value)
    End Sub

    Public Shared Function GetIsMovable(ByRef element As UIElement) As Boolean
        GetIsMovable = CBool(element.GetValue(IsMovableProperty))
    End Function
End Class
// GameService.idl
namespace UserAndCustomControls
{
    [default_interface]
    runtimeclass GameService : Windows.UI.Xaml.DependencyObject
    {
        GameService();
        static Windows.UI.Xaml.DependencyProperty IsMovableProperty{ get; };
        static Boolean GetIsMovable(Windows.UI.Xaml.DependencyObject target);
        static void SetIsMovable(Windows.UI.Xaml.DependencyObject target, Boolean value);
    }
}

// GameService.h
...
    static Windows::UI::Xaml::DependencyProperty IsMovableProperty() { return m_IsMovableProperty; }
    static bool GetIsMovable(Windows::UI::Xaml::DependencyObject const& target) { return winrt::unbox_value<bool>(target.GetValue(m_IsMovableProperty)); }
    static void SetIsMovable(Windows::UI::Xaml::DependencyObject const& target, bool value) { target.SetValue(m_IsMovableProperty, winrt::box_value(value)); }

private:
    static Windows::UI::Xaml::DependencyProperty m_IsMovableProperty;
...

// GameService.cpp
...
Windows::UI::Xaml::DependencyProperty GameService::m_IsMovableProperty =
    Windows::UI::Xaml::DependencyProperty::RegisterAttached(
        L"IsMovable",
        winrt::xaml_typename<bool>(),
        winrt::xaml_typename<UserAndCustomControls::GameService>(),
        Windows::UI::Xaml::PropertyMetadata{ winrt::box_value(false) }
);
...
// GameService.h
#pragma once

#include "pch.h"
//namespace WUX = Windows::UI::Xaml;

namespace UserAndCustomControls {
    public ref class GameService sealed : public WUX::DependencyObject {
    private:
        static WUX::DependencyProperty^ _IsMovableProperty;
    public:
        GameService::GameService();
        void GameService::RegisterDependencyProperties();
        static property WUX::DependencyProperty^ IsMovableProperty
        {
            WUX::DependencyProperty^ get() {
                return _IsMovableProperty;
            }
        };
        static bool GameService::GetIsMovable(WUX::UIElement^ element) {
            return (bool)element->GetValue(_IsMovableProperty);
        };
        static void GameService::SetIsMovable(WUX::UIElement^ element, bool value) {
            element->SetValue(_IsMovableProperty,value);
        }
    };
}

// GameService.cpp
#include "pch.h"
#include "GameService.h"

using namespace UserAndCustomControls;

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Documents;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Media;

GameService::GameService() {};

GameService::RegisterDependencyProperties() {
    DependencyProperty^ GameService::_IsMovableProperty = DependencyProperty::RegisterAttached(
         "IsMovable", Platform::Boolean::typeid, GameService::typeid, ref new PropertyMetadata(false));
}

XAML 태그에서 사용자 지정 연결된 속성 설정

연결된 속성을 정의하고 지원 멤버를 사용자 지정 형식의 일부로 포함한 후에는 XAML 사용에 정의를 사용할 수 있도록 해야 합니다. 이렇게 하려면 관련 클래스를 포함하는 코드 네임스페이스를 참조하는 XAML 네임스페이스를 매핑해야 합니다. 연결된 속성을 라이브러리의 일부로 정의한 경우 해당 라이브러리를 앱 용 앱 패키지의 일부로 포함해야 합니다.

XAML에 대한 XML 네임스페이스 매핑은 일반적으로 XAML 페이지의 루트 요소에 배치됩니다. 예를 들어 이전 코드 조각에 표시된 연결된 속성 정의가 포함된 네임스페이스 GameService에서 명명 UserAndCustomControls 된 클래스의 경우 매핑은 다음과 같이 표시될 수 있습니다.

<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:uc="using:UserAndCustomControls"
  ... >

매핑을 사용하여 Windows 런타임 정의하는 기존 형식을 포함하여 대상 정의와 일치하는 모든 요소에 연결된 속성을 설정할 GameService.IsMovable 수 있습니다.

<Image uc:GameService.IsMovable="True" .../>

매핑된 동일한 XML 네임스페이스 내에 있는 요소에 속성을 설정하는 경우에도 연결된 속성 이름에 접두사를 포함해야 합니다. 이는 접두사에서 소유자 형식을 한정하기 때문입니다. 일반 XML 규칙에 따라 특성이 요소에서 네임스페이스를 상속할 수 있더라도 연결된 속성의 특성은 특성이 포함된 요소와 동일한 XML 네임스페이스 내에 있다고 가정할 수 없습니다. 예를 들어 사용자 지정 형식 GameService.IsMovable (정의가 표시되지 않음)을 설정하고 ImageWithLabelControl 둘 다 동일한 접두사에 매핑된 동일한 코드 네임스페이스에 정의되어 있더라도 XAML은 이 값입니다.

<uc:ImageWithLabelControl uc:GameService.IsMovable="True" .../>

참고 항목

C++/CX로 XAML UI를 작성하는 경우 연결된 속성을 정의하는 사용자 지정 형식을 XAML 페이지에서 사용할 때면 언제든 해당 형식에 대한 헤더를 포함해야 합니다. 각 XAML 페이지에는 관련된 코드 숨김 헤더(.xaml.h)가 있습니다. 여기에 연결된 속성의 소유자 형식 정의에 대한 헤더를 포함해야 합니다(#include 사용).

사용자 지정 연결된 속성을 명령적으로 설정

명령적 코드에서 사용자 지정 연결된 속성에 액세스할 수도 있습니다. 아래 코드는 방법을 보여 줍니다.

<Image x:Name="gameServiceImage"/>
// MainPage.h
...
#include "GameService.h"
...

// MainPage.cpp
...
MainPage::MainPage()
{
    InitializeComponent();

    GameService::SetIsMovable(gameServiceImage(), true);
}
...

사용자 지정 연결된 속성의 값 형식

사용자 지정 연결된 속성의 값 형식으로 사용되는 형식은 사용량, 정의 또는 사용량 및 정의 모두에 영향을 줍니다. 연결된 속성의 값 형식은 Get 및 Set 접근자 메서드의 서명과 RegisterAttached 호출의 propertyType 매개 변수로 여러 위치에서 선언됩니다.

연결된 속성에 대한 가장 일반적인 값 형식(사용자 지정 또는 기타)은 간단한 문자열입니다. 연결된 속성은 일반적으로 XAML 특성 사용을 위한 것이며 값 형식으로 문자열을 사용하면 속성이 경량으로 유지되기 때문입니다. 정수, double 또는 열거형 값과 같이 문자열 메서드로 네이티브 변환이 있는 다른 기본 형식도 연결된 속성의 값 형식으로 일반적입니다. 네이티브 문자열 변환을 지원하지 않는 다른 값 형식을 연결된 속성 값으로 사용할 수 있습니다. 그러나 이 경우 사용량 또는 구현 중 하나를 선택해야 합니다.

  • 연결된 속성을 그대로 둘 수 있지만 연결된 속성은 연결된 속성이 속성 요소이고 값이 개체 요소로 선언된 경우에만 사용을 지원할 수 있습니다. 이 경우 속성 형식은 개체 요소로 XAML 사용을 지원해야 합니다. 기존 Windows 런타임 참조 클래스의 경우 XAML 구문을 검사 형식이 XAML 개체 요소 사용을 지원하는지 확인합니다.
  • 연결된 속성은 그대로 둘 수 있지만 문자열로 표현할 수 있는 Binding 또는 StaticResource와 같은 XAML 참조 기술을 통해 특성 사용에서만 사용할 수 있습니다.

Canvas.Left 예제에 대한 자세한 정보

연결된 속성 사용의 이전 예제에서는 Canvas.Left 연결된 속성을 설정하는 다양한 방법을 보여 줍니다. 그러나 그것이 언제, 그리고 어떻게 캔버스가 개체와 상호 작용하는 방식을 변경할까요? 연결된 속성을 구현하는 경우 연결된 속성 소유자 클래스가 연결된 속성 값을 다른 개체에서 찾을 경우 연결된 속성 값으로 수행할 다른 작업을 보는 것이 흥미롭기 때문에 이 특정 예제를 추가로 살펴보겠습니다.

Canvas기본 함수는 UI에서 절대 위치 레이아웃 컨테이너여야 합니다. Canvas의 자식은 기본 클래스 정의 속성 Children에 저장됩니다. 모든 패널 중 캔버스 는 절대 위치 지정을 사용하는 유일한 패널입니다. 그것은 캔버스 및 UIElement의 자식 요소인 특정 UIElement 사례에만 문제가 될 수 있는 속성을 추가하기 위해 공통 UIElement 형식의 개체 모델을 비대화했을 것입니다. UIElement에서 사용할 수 있는 연결된 속성이 되도록 Canvas의 레이아웃 컨트롤 속성을 정의하면 개체 모델이 깨끗이 유지됩니다.

실용적인 패널이 되기 위해 Canvas에는 프레임워크 수준 측정값 및 정렬 메서드를 재정의하는 동작이 있습니다. 바로 여기서 Canvas 자식에 연결된 속성 값에 대해 검사합니다. 측정값 및 정렬 패턴의 일부는 모든 콘텐츠를 반복하는 루프이며 패널에는 패널의 자식으로 간주되어야 하는 내용을 명시적으로 표시하는 Children 속성이 있습니다. 따라서 Canvas 레이아웃 동작은 이러한 자식을 반복하고 각 자식에 대해 Static Canvas.GetLeftCanvas.GetTop을 호출하여 연결된 속성에 기본값이 아닌 값이 포함되어 있는지 확인합니다(기본값: 0). 그런 다음 각 자식이 제공한 특정 값에 따라 캔버스 사용 가능한 레이아웃 공간에 각 자식의 위치를 지정하고 Arrange를 사용하여 수행하는 데 이러한 값을 사용합니다.

코드는 다음 의사 코드처럼 표시됩니다.

protected override Size ArrangeOverride(Size finalSize)
{
    foreach (UIElement child in Children)
    {
        double x = (double) Canvas.GetLeft(child);
        double y = (double) Canvas.GetTop(child);
        child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
    }
    return base.ArrangeOverride(finalSize); 
    // real Canvas has more sophisticated sizing
}

참고 항목

패널 작동 방법에 대한 자세한 내용은 XAML 사용자 지정 패널 개요를 참조하세요.