Sdílet prostřednictvím


Vlastní připojené vlastnosti

Připojená vlastnost je koncept XAML. Připojené vlastnosti jsou obvykle definovány jako specializovaná forma vlastnosti závislosti. Toto téma vysvětluje, jak implementovat připojenou vlastnost jako vlastnost závislosti a jak definovat konvenci přístupového objektu, která je nezbytná pro použití připojené vlastnosti v XAML.

Požadavky

Předpokládáme, že rozumíte vlastnostem závislostí z pohledu příjemce existujících vlastností závislostí a že jste si přečetli přehled vlastností závislostí. Měli byste si také přečíst Přehled připojených vlastností. Pokud chcete postupovat podle příkladů v tomto tématu, měli byste také porozumět jazyku XAML a vědět, jak psát základní aplikaci prostředí Windows Runtime pomocí C++, C# nebo Visual Basicu.

Scénáře pro připojené vlastnosti

Můžete vytvořit připojenou vlastnost, pokud existuje důvod, proč mít mechanismus nastavení vlastností k dispozici pro třídy jiné než definování třídy. Podpora rozložení a služeb patří mezi nejběžnější scénáře. Příklady existujících vlastností rozložení jsou Canvas.ZIndex a Canvas.Top. Ve scénáři rozložení mohou prvky, které existují jako podřízené prvky k prvkům, které ovládají rozložení, individuálně vyjádřit požadavky na nadřazené prvky, přičemž jednotlivě nastavují hodnotu vlastnosti, kterou rodič definuje jako připojenou vlastnost. Příkladem scénáře podpory služeb v rozhraní API prostředí Windows Runtime je sada připojených vlastností ScrollVieweru, jako je ScrollViewer.IsZoomChainingEnabled.

Výstraha

Existující omezení implementace XAML prostředí Windows Runtime je, že nemůžete animovat vlastní připojenou vlastnost.

Registrace vlastní připojené vlastnosti

Pokud definujete připojenou vlastnost přísně pro použití u jiných typů, třída, ve které je vlastnost registrována, nemusí být odvozena z DependencyObject. Pokud použijete typický model, ve kterém je připojená vlastnost zároveň vlastností závislosti, musíte mít cílový parametr pro použití přístupových metod na DependencyObject, abyste mohli použít úložiště podpůrných vlastností.

Definujte svou připojenou vlastnost jako závislou vlastnost deklarováním veřejnéstatickéjen pro čtení vlastnosti typu DependencyProperty. Tuto vlastnost definujete pomocí návratové hodnoty RegisterAttached metoda. Název vlastnosti se musí shodovat s názvem připojené vlastnosti, který zadáte jako parametr názevRegisterAttached s řetězcem "Property" přidaným na konec. Jedná se o zavedenou konvenci pro pojmenování identifikátorů vlastností závislostí ve vztahu k vlastnostem, které představují.

Hlavní oblast, ve které se definování vlastní připojené vlastnosti liší od vlastní vlastnosti závislosti, je způsob, jakým definujete přístupové objekty nebo obálky. Místo použití techniky obálky popsané ve vlastnostech vlastních závislostí musíte také poskytnout statické metody GetPropertyName a SetPropertyName jako přístupové metody pro připojenou vlastnost. Přístupové objekty se používají většinou analyzátorem XAML, i když je může jakýkoli jiný volající použít také k nastavení hodnot ve scénářích, které nejsou XAML.

Důležité

Pokud akcesory nedefinujete správně, procesor XAML nemůže přistupovat k vaší připojené vlastnosti a každý, kdo se ji pokusí použít, pravděpodobně obdrží chybu analyzátoru XAML. Nástroje pro návrh a kódování také často spoléhají na konvence "*Property" pro pojmenování identifikátorů, když v odkazovaném sestavení narazí na vlastní vlastnost závislosti.

Accessors

Podpis přístupového objektu GetPropertyName musí být tento.

public static valueTypeGetPropertyName(DependencyObject target)

Pro Microsoft Visual Basic je to.

Public Shared Function Get PropertyName(ByVal target As DependencyObject) As valueType)

Cílový objekt může být v implementaci konkrétnějšího typu, ale musí být odvozen z DependencyObject. Návratová hodnota ValueType může být také konkrétnějšího typu v implementaci. Základní typ objektu je přijatelný, ale často budete chtít, aby připojená vlastnost vynucuje bezpečnost typů. Použití typování v popisech getter a setter je doporučená technika zabezpečení typu.

Podpis pro objekt SetPropertyName musí být tento.

public static void Set PropertyName(DependencyObject target ,valueType value)

Pro Visual Basic je to toto.

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

Cílový objekt může být v implementaci konkrétnějšího typu, ale musí být odvozen z DependencyObject. Objekt value a jeho valueType mohou být konkrétnějšího typu v implementaci. Nezapomeňte, že hodnota pro tuto metodu je vstup, který pochází z procesoru XAML, když narazí na vaši připojenou vlastnost v kódu. Pro datový typ, který používáte, musí existovat podpora převodu datového typu nebo existující podpora rozšíření značkovacího jazyka, aby bylo možné příslušný typ vytvořit z hodnoty atributu (která je nakonec jen řetězec). Základní typ objektu je přijatelný, ale často budete chtít další bezpečnost typů. Chcete-li toho dosáhnout, umístěte vynucení typu do přístupových objektů.

Poznámka:

Je také možné definovat připojenou vlastnost, kde zamýšlené použití je prostřednictvím syntaxe elementu vlastnosti. V takovém případě pro hodnoty nepotřebujete převod typu, ale potřebujete zajistit, aby hodnoty, které máte v úmyslu, bylo možné vytvořit v XAML. VisualStateManager.VisualStateGroups je příkladem existující připojené vlastnosti, která podporuje pouze použití elementu property.

Příklad kódu

Tento příklad ukazuje registraci vlastnosti závislosti (pomocí Metody RegisterAttached ), stejně jako Get a Set přístupové objekty pro vlastní připojenou vlastnost. V příkladu je IsMovablenázev připojené vlastnosti . Proto musí být přístupové objekty pojmenovány GetIsMovable a SetIsMovable. Vlastníkem připojené vlastnosti je třída služby s názvem GameService, která nemá vlastní uživatelské rozhraní; jejím účelem je poskytovat služby související s připojenými vlastnostmi při použití připojené vlastnosti GameService.IsMovable.

Definování připojené vlastnosti v jazyce C++/CX je trochu složitější. Musíte se rozhodnout, jak použít faktor mezi hlavičkou a souborem kódu. Identifikátor byste také měli zveřejnit jako vlastnost s pouze přístupovým objektem get , a to z důvodů probíraných ve vlastnostech vlastní závislosti. V jazyce C++/CX je nutné explicitně definovat vztah mezi polem a vlastností, a nespoléhat se na klíčové slovo .NET readonly a implicitní podporu jednoduchých vlastností. Musíte také provést registraci připojené vlastnosti v pomocné funkci, která se spustí jen jednou, když se aplikace poprvé spustí, ale před načtením všech stránek XAML, které potřebují připojenou vlastnost. Typické místo pro volání pomocných funkcí registrace vlastností pro všechny závislé nebo připojené vlastnosti je z konstruktoru / v kódu souboru 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));
}

Nastavení vlastní připojené vlastnosti pomocí zápisu XAML

Jakmile definujete připojenou vlastnost a zahrnete její členy podpory jako součást vlastního typu, musíte pak zpřístupnit definice pro použití XAML. K tomu je nutné namapovat obor názvů XAML, který bude odkazovat na obor názvů kódu, který obsahuje příslušnou třídu. V případech, kdy jste definovali připojenou vlastnost jako součást knihovny, musíte tuto knihovnu zahrnout jako součást balíčku aplikace pro aplikaci.

Mapování oboru názvů XML pro XAML je obvykle umístěné v kořenovém prvku stránky XAML. Například pro třídu pojmenovanou GameService v oboru názvů UserAndCustomControls , která obsahuje definice připojené vlastnosti zobrazené v předchozích fragmentech kódu, může mapování vypadat takto.

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

Pomocí mapování můžete nastavit GameService.IsMovable připojenou vlastnost u libovolného prvku, který odpovídá vaší cílové definici, včetně existujícího typu, který prostředí Windows Runtime definuje.

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

Pokud vlastnost nastavujete u elementu, který je také ve stejném mapovaném oboru názvů XML, musíte stále zahrnout předponu názvu připojené vlastnosti. Důvodem je to, že předpona kvalifikuje typ vlastníka. Atribut vlastnosti, která je připojena, se nemůže předpokládat, že je ve stejném XML oboru názvů jako prvek, kde je atribut obsažen, i když podle běžných pravidel XML mohou atributy dědit obor názvů z elementů. Pokud například nastavujete GameService.IsMovable pro vlastní typ ImageWithLabelControl (definice není zobrazena), a i když jsou oba definovány ve stejném oboru názvů kódu, který je namapován na stejnou předponu, XAML by přesto vypadal takto.

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

Poznámka:

Pokud píšete uživatelské rozhraní XAML pomocí C++/CX, musíte zahrnout hlavičku pro vlastní typ, který definuje připojenou vlastnost, kdykoli bude stránka XAML tento typ používat. Každá stránka XAML má přidruženou hlavičku code-behind (.xaml.h). Tady byste měli vložit (pomocí #include) hlavičku pro definici typu vlastníka dané připojené vlastnosti.

Imperativní nastavení vlastní připojené vlastnosti

K vlastní připojené vlastnosti můžete přistupovat také z imperativního kódu. Následující kód ukazuje, jak na to.

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

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

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

Typ hodnoty vlastní připojené vlastnosti

Typ, který se používá jako typ hodnoty vlastní připojené vlastnosti, ovlivňuje použití, definici nebo použití i definici. Typ hodnoty připojené vlastnosti je deklarován na několika místech: v deklaracích přístupových metod Get a Set, a také jako parametr propertyType v rámci volání RegisterAttached.

Nejběžnějším typem hodnoty pro připojené vlastnosti (vlastní nebo jinak) je jednoduchý řetězec. Důvodem je to, že připojené vlastnosti jsou obecně určeny pro použití atributu XAML a použití řetězce jako typ hodnoty udržuje vlastnosti zjednodušené. Další primitivy, které mají nativní převod na řetězcové metody, jako jsou integer, double nebo hodnota výčtu, jsou také běžné jako typy hodnot pro připojené vlastnosti. Jako hodnotu připojené vlastnosti můžete použít jiné typy hodnot , které nepodporují převod nativních řetězců. To však znamená, že je třeba se rozhodnout mezi použitím nebo implementací:

  • Připojenou vlastnost můžete ponechat tak, jak je, ale připojená vlastnost může podporovat použití pouze tam, kde připojená vlastnost je prvek vlastnosti a hodnota je deklarována jako objekt element. V tomto případě typ vlastnosti musí podporovat použití XAML jako element objektu. U existujících referenčních tříd prostředí Windows Runtime zkontrolujte syntaxi XAML a ujistěte se, že typ podporuje použití elementu objektu XAML.
  • Připojenou vlastnost můžete ponechat tak, jak je, ale použít ji pouze v použití atributu prostřednictvím referenční techniky XAML, jako je Binding nebo StaticResource , které lze vyjádřit jako řetězec.

Další informace o příkladu Canvas.Left

V předchozích příkladech použití připojených vlastností jsme ukázali různé způsoby, jak nastavit připojenou vlastnost Canvas.Left . Co se ale změní v tom, jak plátno interaguje s vaším objektem a kdy se to stane? Podrobněji si probereme tento konkrétní příklad, protože pokud implementujete připojenou vlastnost, je zajímavé zjistit, co dalšího typická třída vlastníka připojené vlastnosti hodlá provést s jeho připojenými hodnotami vlastností, pokud je najde v jiných objektech.

Hlavní funkcí Canvasu je fungovat jako kontejner s absolutním umístěním pro rozložení prvků v uživatelském rozhraní. Podřízené položky Canvas jsou uloženy ve vlastnosti definované základní třídou Children. Ze všech panelů Plátno je jediným panelem, který používá absolutní umístění. Objektový model společného typu UIElement by se nafoukl, kdyby se přidaly vlastnosti, které by mohly být relevantní pouze pro Canvas a pro ty konkrétní případy UIElement, kdy by byly podřízenými prvky UIElement. Definování vlastností rozložení ovládacího prvku Canvas jako připojených vlastností, které může využívat jakýkoli UIElement, udržuje objektový model čistý.

Aby byl Canvas praktickým panelem, má chování, které přepisuje metody Měření a Uspořádání na úrovni frameworku. Tady Canvas ve skutečnosti kontroluje hodnoty připojených vlastností v podřízených objektech. Součástí vzorů Měření i Rozmístění je smyčka, která iteruje přes libovolný obsah, a panel má vlastnost Children, která výslovně určuje, co má být považováno za podřízené panelu. Takže chování layoutu Canvas iteruje přes tyto podřízené objekty a provádí statické volání Canvas.GetLeft a Canvas.GetTop u každého podřízeného objektu, aby zjistilo, jestli tyto připojené vlastnosti obsahují nevýchozí hodnotu (výchozí hodnota je 0). Tyto hodnoty se pak používají k absolutnímu umístění jednotlivých podřízených prvků v dostupném rozložení součásti Canvas podle konkrétních hodnot poskytovaných každým podřízeným prvkem a je to dokončeno pomocí funkce Arrange.

Kód vypadá podobně jako tento pseudokód.

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
}

Poznámka:

Další informace o fungování panelů najdete v přehledu vlastních panelů XAML.