Properti terlampir kustom

Properti terlampir adalah konsep XAML. Properti terlampir biasanya didefinisikan sebagai bentuk khusus properti dependensi. Topik ini menjelaskan cara menerapkan properti terlampir sebagai properti dependensi dan cara menentukan konvensi aksesor yang diperlukan agar properti terlampir Anda dapat digunakan di XAML.

Prasyarat

Kami berasumsi bahwa Anda memahami properti dependensi dari perspektif konsumen properti dependensi yang ada, dan Anda telah membaca gambaran umum properti Dependensi. Anda juga harus membaca Gambaran umum properti Terlampir. Untuk mengikuti contoh dalam topik ini, Anda juga harus memahami XAML dan tahu cara menulis aplikasi Windows Runtime dasar menggunakan C++, C#, atau Visual Basic.

Skenario untuk properti terlampir

Anda mungkin membuat properti terlampir ketika ada alasan untuk memiliki mekanisme pengaturan properti yang tersedia untuk kelas selain kelas yang menentukan. Skenario yang paling umum untuk ini adalah tata letak dan dukungan layanan. Contoh properti tata letak yang ada adalah Canvas.ZIndex dan Canvas.Top. Dalam skenario tata letak, elemen yang ada sebagai elemen turunan ke elemen pengontrol tata letak dapat mengekspresikan persyaratan tata letak ke elemen induknya satu per satu, masing-masing mengatur nilai properti yang didefinisikan induk sebagai properti terlampir. Contoh skenario dukungan layanan di Windows Runtime API adalah kumpulan properti terlampir dari ScrollViewer, seperti ScrollViewer.IsZoomChainingEnabled.

Peringatan

Batasan implementasi Windows Runtime XAML yang ada adalah Anda tidak dapat menganimasikan properti terlampir kustom Anda.

Mendaftarkan properti terlampir kustom

Jika Anda mendefinisikan properti terlampir secara ketat untuk digunakan pada jenis lain, kelas tempat properti terdaftar tidak harus berasal dari DependencyObject. Tetapi Anda harus memiliki parameter target untuk aksesor menggunakan DependencyObject jika Anda mengikuti model umum memiliki properti terlampir Anda juga menjadi properti dependensi, sehingga Anda dapat menggunakan penyimpanan properti backing.

Tentukan properti terlampir Anda sebagai properti dependensi dengan mendeklarasikan properti readonly statispublikdari jenis DependencyProperty. Anda menentukan properti ini dengan menggunakan nilai pengembalian metode RegisterAttached. Nama properti harus cocok dengan nama properti terlampir yang Anda tentukan sebagai parameter nama RegisterAttached, dengan string "Properti" ditambahkan ke akhir. Ini adalah konvensi yang ditetapkan untuk memberi nama pengidentifikasi properti dependensi sehubungan dengan properti yang diwakilinya.

Area utama di mana menentukan properti terlampir kustom berbeda dari properti dependensi kustom adalah bagaimana Anda menentukan aksesor atau pembungkus. Alih-alih menggunakan teknik pembungkus yang dijelaskan dalam Properti dependensi kustom, Anda juga harus menyediakan metode GetPropertyName dan SetPropertyName statis sebagai aksesor untuk properti terlampir. Pengakses sebagian besar digunakan oleh pengurai XAML, meskipun pemanggil lain juga dapat menggunakannya untuk mengatur nilai dalam skenario non-XAML.

Penting

Jika Anda tidak menentukan aksesor dengan benar, prosesor XAML tidak dapat mengakses properti terlampir Anda dan siapa pun yang mencoba menggunakannya mungkin akan mendapatkan kesalahan pengurai XAML. Selain itu, alat desain dan pengkodan sering mengandalkan konvensi "*Properti" untuk penamaan pengidentifikasi ketika mereka menemukan properti dependensi kustom dalam rakitan yang direferensikan.

Pengakses

Tanda tangan untuk aksesor GetPropertyName harus ini.

public staticvalueTypeGetPropertyName(DependencyObject target)

Untuk Microsoft Visual Basic, ini adalah ini.

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

Objek target dapat memiliki jenis yang lebih spesifik dalam implementasi Anda, tetapi harus berasal dari DependencyObject. Nilai yang dikembalikan valueType juga dapat berupa jenis yang lebih spesifik dalam implementasi Anda. Jenis Objek dasar dapat diterima, tetapi seringkali Anda ingin properti terlampir Anda memberlakukan keamanan jenis. Penggunaan pengetikan dalam tanda tangan getter dan setter adalah teknik keamanan jenis yang direkomendasikan.

Tanda tangan untuk aksesor SetPropertyName harus ini.

public static void SetPropertyName(DependencyObject target ,valueType value)

Untuk Visual Basic, inilah.

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

Objek target dapat memiliki jenis yang lebih spesifik dalam implementasi Anda, tetapi harus berasal dari DependencyObject. Objek nilai dan valueType-nyadapat berupa jenis yang lebih spesifik dalam implementasi Anda. Ingatlah bahwa nilai untuk metode ini adalah input yang berasal dari prosesor XAML ketika menemukan properti terlampir Anda dalam markup. Harus ada konversi jenis atau dukungan ekstensi markup yang ada untuk jenis yang Anda gunakan, sehingga jenis yang sesuai dapat dibuat dari nilai atribut (yang pada akhirnya hanya string). Jenis Objek dasar dapat diterima, tetapi seringkali Anda menginginkan keamanan jenis lebih lanjut. Untuk mencapainya, letakkan penegakan jenis di aksesor.

Catatan

Anda juga dapat menentukan properti terlampir di mana penggunaan yang dimaksudkan adalah melalui sintaks elemen properti. Dalam hal ini Anda tidak memerlukan konversi jenis untuk nilai, tetapi Anda perlu memastikan bahwa nilai yang Anda inginkan dapat dibangun di XAML. VisualStateManager.VisualStateGroups adalah contoh properti terlampir yang ada yang hanya mendukung penggunaan elemen properti.

Contoh kode

Contoh ini menunjukkan pendaftaran properti dependensi (menggunakan metode RegisterAttached ), serta aksesor Dapatkan dan Atur , untuk properti terlampir kustom. Dalam contoh, nama properti yang dilampirkan adalah IsMovable. Oleh karena itu, aksesor harus diberi nama GetIsMovable dan SetIsMovable. Pemilik properti terlampir adalah kelas layanan bernama GameService yang tidak memiliki UI sendiri; tujuannya hanya untuk menyediakan layanan properti terlampir ketika properti terlampir GameService.IsMovable digunakan.

Menentukan properti terlampir di C++/CX sedikit lebih kompleks. Anda harus memutuskan cara memperhitungkan antara header dan file kode. Selain itu, Anda harus mengekspos pengidentifikasi sebagai properti hanya dengan aksesor get , karena alasan yang dibahas dalam Properti dependensi kustom. Di C++/CX Anda harus menentukan hubungan bidang properti ini secara eksplisit daripada mengandalkan kata kunci baca-saja .NET dan dukungan implisit dari properti sederhana. Anda juga perlu melakukan pendaftaran properti terlampir dalam fungsi pembantu yang hanya dijalankan sekali, ketika aplikasi pertama kali dimulai tetapi sebelum halaman XAML yang memerlukan properti terlampir dimuat. Tempat umum untuk memanggil fungsi pembantu pendaftaran properti Anda untuk setiap dan semua dependensi atau properti terlampir berasal dari dalam konstruktor Aplikasi Aplikasi / dalam kode untuk file app.xaml Anda.

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));
}

Mengatur properti terlampir kustom Anda dari markup XAML

Setelah Anda menentukan properti terlampir dan menyertakan anggota dukungannya sebagai bagian dari jenis kustom, Anda kemudian harus membuat definisi tersedia untuk penggunaan XAML. Untuk melakukan ini, Anda harus memetakan namespace XAML yang akan mereferensikan namespace kode yang berisi kelas yang relevan. Dalam kasus di mana Anda telah menentukan properti terlampir sebagai bagian dari pustaka, Anda harus menyertakan pustaka tersebut sebagai bagian dari paket aplikasi untuk aplikasi.

Pemetaan namespace XML untuk XAML biasanya ditempatkan di elemen akar halaman XAML. Misalnya, untuk kelas bernama GameService di namespace UserAndCustomControls yang berisi definisi properti terlampir yang ditampilkan dalam cuplikan sebelumnya, pemetaan mungkin terlihat seperti ini.

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

Dengan menggunakan pemetaan, Anda dapat mengatur GameService.IsMovable properti terlampir pada elemen apa pun yang cocok dengan definisi target Anda, termasuk jenis yang sudah ada yang ditentukan Windows Runtime.

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

Jika Anda mengatur properti pada elemen yang juga berada dalam namespace XML yang dipetakan yang sama, Anda masih harus menyertakan awalan pada nama properti terlampir. Ini karena awalan memenuhi syarat jenis pemilik. Atribut properti terlampir tidak dapat diasumsikan berada dalam namespace XML yang sama dengan elemen tempat atribut disertakan, meskipun, menurut aturan XML normal, atribut dapat mewarisi namespace dari elemen. Misalnya, jika Anda mengatur GameService.IsMovable pada jenis ImageWithLabelControl kustom (definisi tidak ditampilkan), dan bahkan jika keduanya didefinisikan dalam namespace kode yang sama yang dipetakan ke awalan yang sama, XAML akan tetap seperti ini.

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

Catatan

Jika Anda menulis UI XAML dengan C++/CX, maka Anda harus menyertakan header untuk jenis kustom yang menentukan properti terlampir, kapan saja halaman XAML menggunakan jenis tersebut. Setiap halaman XAML memiliki header code-behind terkait (.xaml.h). Di sinilah Anda harus menyertakan (menggunakan #include) header untuk definisi jenis pemilik properti terlampir.

Mengatur properti terlampir kustom Anda secara imperatif

Anda juga dapat mengakses properti terlampir kustom dari kode imperatif. Kode di bawah ini menunjukkan caranya.

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

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

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

Jenis nilai properti terlampir kustom

Jenis yang digunakan sebagai jenis nilai properti terlampir kustom memengaruhi penggunaan, definisi, atau penggunaan dan definisi. Jenis nilai properti terlampir dideklarasikan di beberapa tempat: dalam tanda tangan metode aksesor Dapatkan dan Atur , dan juga sebagai parameter propertyType dari panggilan RegisterAttached .

Jenis nilai yang paling umum untuk properti terlampir (kustom atau sebaliknya) adalah string sederhana. Ini karena properti terlampir umumnya ditujukan untuk penggunaan atribut XAML, dan menggunakan string karena jenis nilai menjaga properti tetap ringan. Primitif lain yang memiliki konversi asli ke metode string, seperti bilangan bulat, ganda, atau nilai enumerasi, juga umum sebagai jenis nilai untuk properti terlampir. Anda dapat menggunakan jenis nilai lain—yang tidak mendukung konversi string asli—sebagai nilai properti terlampir. Namun, ini memerlukan membuat pilihan tentang penggunaan atau implementasi:

  • Anda dapat meninggalkan properti terlampir apa adanya, tetapi properti terlampir hanya dapat mendukung penggunaan di mana properti terlampir adalah elemen properti, dan nilai dinyatakan sebagai elemen objek. Dalam hal ini, jenis properti memang harus mendukung penggunaan XAML sebagai elemen objek. Untuk kelas referensi Windows Runtime yang ada, periksa sintaks XAML untuk memastikan bahwa jenis mendukung penggunaan elemen objek XAML.
  • Anda dapat meninggalkan properti terlampir apa adanya, tetapi menggunakannya hanya dalam penggunaan atribut melalui teknik referensi XAML seperti Pengikatan atau StaticResource yang dapat diekspresikan sebagai string.

Lebih lanjut tentang contoh Canvas.Left

Dalam contoh penggunaan properti terlampir sebelumnya, kami menunjukkan berbagai cara untuk mengatur properti Terlampir Canvas.Left. Tetapi apa yang berubah tentang bagaimana Canvas berinteraksi dengan objek Anda, dan kapan itu terjadi? Kami akan memeriksa contoh khusus ini lebih lanjut, karena jika Anda menerapkan properti terlampir, menarik untuk melihat apa lagi kelas pemilik properti terlampir yang khas yang ingin dilakukan dengan nilai properti terlampirnya jika menemukannya pada objek lain.

Fungsi utama Kanvas adalah menjadi kontainer tata letak yang diposisikan absolut di UI. Anak-anak Kanvas disimpan dalam properti kelas dasar yang ditentukan Anak-anak. Dari semua panel Canvas adalah satu-satunya yang menggunakan posisi absolut. Ini akan membentangkan model objek dari jenis UIElement umum untuk menambahkan properti yang mungkin hanya menjadi perhatian Kanvas dan kasus UIElement tertentu di mana mereka adalah elemen turunan dari UIElement. Menentukan properti kontrol tata letak Kanvas menjadi properti terlampir yang dapat digunakan UIElement mana pun agar model objek tetap bersih.

Untuk menjadi panel praktis, Canvas memiliki perilaku yang mengambil alih metode Pengukuran dan Pengaturan tingkat kerangka kerja. Di sinilah Canvas benar-benar memeriksa nilai properti terlampir pada anak-anaknya. Bagian dari pola Pengukuran dan Pengaturan adalah perulangan yang melakukan iterasi atas konten apa pun, dan panel memiliki properti Anak yang membuatnya eksplisit apa yang seharusnya dianggap sebagai anak dari panel. Jadi perilaku tata letak Kanvas berulang melalui anak-anak ini, dan membuat panggilan Canvas.GetLeft dan Canvas.GetTop statis pada setiap anak untuk melihat apakah properti terlampir tersebut berisi nilai non-default (defaultnya adalah 0). Nilai-nilai ini kemudian digunakan untuk benar-benar memposisikan setiap anak di ruang tata letak yang tersedia Kanvas sesuai dengan nilai tertentu yang disediakan oleh setiap anak, dan diterapkan menggunakan Atur.

Kodenya terlihat seperti pseudocode ini.

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
}

Catatan

Untuk informasi selengkapnya tentang cara kerja panel, lihat Gambaran umum panel kustom XAML.