Bagikan melalui


Panggilan Balik dan Validasi Properti Dependensi

Topik ini menjelaskan cara membuat properti dependensi menggunakan implementasi kustom alternatif untuk fitur terkait properti seperti penentuan validasi, panggilan balik yang dipanggil setiap kali nilai efektif properti diubah, dan mengambil alih kemungkinan pengaruh luar pada penentuan nilai. Topik ini juga membahas skenario di mana memperluas perilaku sistem properti default dengan menggunakan teknik ini sesuai.

Prasyarat

Topik ini mengasumsikan bahwa Anda memahami skenario dasar penerapan properti dependensi, dan bagaimana metadata diterapkan ke properti dependensi kustom. Lihat Properti Dependensi Kustom dan Metadata Properti Dependensi untuk konteksnya.

Panggilan Balik Validasi

Panggilan balik validasi dapat ditetapkan ke properti dependensi saat Anda pertama kali mendaftarkannya. Panggilan balik validasi bukan bagian dari metadata properti; ini adalah input langsung dari Register metode . Oleh karena itu, setelah panggilan balik validasi dibuat untuk properti dependensi, itu tidak dapat ditimpa oleh implementasi baru.

public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
    "CurrentReading",
    typeof(double),
    typeof(Gauge),
    new FrameworkPropertyMetadata(
        Double.NaN,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        new PropertyChangedCallback(OnCurrentReadingChanged),
        new CoerceValueCallback(CoerceCurrentReading)
    ),
    new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
  get { return (double)GetValue(CurrentReadingProperty); }
  set { SetValue(CurrentReadingProperty, value); }
}
Public Shared ReadOnly CurrentReadingProperty As DependencyProperty =
    DependencyProperty.Register("CurrentReading",
        GetType(Double), GetType(Gauge),
        New FrameworkPropertyMetadata(Double.NaN,
            FrameworkPropertyMetadataOptions.AffectsMeasure,
            New PropertyChangedCallback(AddressOf OnCurrentReadingChanged),
            New CoerceValueCallback(AddressOf CoerceCurrentReading)),
        New ValidateValueCallback(AddressOf IsValidReading))

Public Property CurrentReading() As Double
    Get
        Return CDbl(GetValue(CurrentReadingProperty))
    End Get
    Set(ByVal value As Double)
        SetValue(CurrentReadingProperty, value)
    End Set
End Property

Panggilan balik diimplementasikan sedih sehingga mereka diberikan nilai objek. Mereka mengembalikan true jika nilai yang disediakan valid untuk properti; jika tidak, mereka mengembalikan false. Diasumsikan bahwa properti adalah jenis yang benar per jenis yang terdaftar di sistem properti, sehingga memeriksa jenis dalam panggilan balik tidak biasanya dilakukan. Panggilan balik digunakan oleh sistem properti dalam berbagai operasi yang berbeda. Ini termasuk inisialisasi jenis awal dengan nilai default, perubahan terprogram dengan memanggil SetValue, atau mencoba mengambil alih metadata dengan nilai default baru yang disediakan. Jika panggilan balik validasi dipanggil oleh salah satu operasi ini, dan mengembalikan false, maka pengecualian akan dinaikkan. Penulis aplikasi harus siap untuk menangani pengecualian ini. Penggunaan umum panggilan balik validasi adalah memvalidasi nilai enumerasi, atau membatasi nilai bilangan bulat atau ganda ketika properti menetapkan pengukuran yang harus nol atau lebih besar.

Panggilan balik validasi secara khusus dimaksudkan untuk menjadi validator kelas, bukan validator instans. Parameter panggilan balik tidak mengomunikasikan secara spesifik DependencyObject tempat properti yang akan divalidasi diatur. Oleh karena itu, panggilan balik validasi tidak berguna untuk memberlakukan kemungkinan "dependensi" yang mungkin memengaruhi nilai properti, di mana nilai khusus instans properti bergantung pada faktor-faktor seperti nilai khusus instans dari properti lain, atau status run-time.

Berikut ini adalah contoh kode untuk skenario panggilan balik validasi yang sangat sederhana: memvalidasi bahwa properti yang ditik sebagai Double primitif bukan PositiveInfinity atau NegativeInfinity.

public static bool IsValidReading(object value)
{
    Double v = (Double)value;
    return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
}
Public Shared Function IsValidReading(ByVal value As Object) As Boolean
    Dim v As Double = CType(value, Double)
    Return ((Not v.Equals(Double.NegativeInfinity)) AndAlso
            (Not v.Equals(Double.PositiveInfinity)))
End Function

Panggilan Balik Nilai Koersi dan Peristiwa yang Diubah Properti

Panggilan balik nilai koersi meneruskan instans tertentu DependencyObject untuk properti, seperti halnya PropertyChangedCallback implementasi yang dipanggil oleh sistem properti setiap kali nilai properti dependensi berubah. Dengan menggunakan kedua panggilan balik ini dalam kombinasi, Anda dapat membuat serangkaian properti pada elemen di mana perubahan dalam satu properti akan memaksa pemaksaan atau evaluasi ulang properti lain.

Skenario umum untuk menggunakan tautan properti dependensi adalah ketika Anda memiliki properti berbasis antarmuka pengguna di mana elemen menyimpan satu properti masing-masing untuk nilai minimum dan maksimum, dan properti ketiga untuk nilai aktual atau saat ini. Di sini, jika maksimum disesuaikan sedih sehingga nilai saat ini melebihi maksimum baru, Anda ingin memaksa nilai saat ini menjadi tidak lebih besar dari maksimum baru, dan hubungan serupa untuk minimum hingga saat ini.

Berikut ini adalah contoh kode yang sangat singkat hanya untuk salah satu dari tiga properti dependensi yang menggambarkan hubungan ini. Contoh menunjukkan bagaimana CurrentReading properti kumpulan Min/Max/Current dari properti terkait *Membaca didaftarkan. Ini menggunakan validasi seperti yang ditunjukkan di bagian sebelumnya.

public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
    "CurrentReading",
    typeof(double),
    typeof(Gauge),
    new FrameworkPropertyMetadata(
        Double.NaN,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        new PropertyChangedCallback(OnCurrentReadingChanged),
        new CoerceValueCallback(CoerceCurrentReading)
    ),
    new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
  get { return (double)GetValue(CurrentReadingProperty); }
  set { SetValue(CurrentReadingProperty, value); }
}
Public Shared ReadOnly CurrentReadingProperty As DependencyProperty =
    DependencyProperty.Register("CurrentReading",
        GetType(Double), GetType(Gauge),
        New FrameworkPropertyMetadata(Double.NaN,
            FrameworkPropertyMetadataOptions.AffectsMeasure,
            New PropertyChangedCallback(AddressOf OnCurrentReadingChanged),
            New CoerceValueCallback(AddressOf CoerceCurrentReading)),
        New ValidateValueCallback(AddressOf IsValidReading))

Public Property CurrentReading() As Double
    Get
        Return CDbl(GetValue(CurrentReadingProperty))
    End Get
    Set(ByVal value As Double)
        SetValue(CurrentReadingProperty, value)
    End Set
End Property

Properti yang diubah panggilan balik untuk Saat Ini digunakan untuk meneruskan perubahan ke properti dependen lainnya, dengan secara eksplisit memanggil panggilan balik nilai koersi yang terdaftar untuk properti lain tersebut:

private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  d.CoerceValue(MinReadingProperty);
  d.CoerceValue(MaxReadingProperty);
}
Private Shared Sub OnCurrentReadingChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    d.CoerceValue(MinReadingProperty)
    d.CoerceValue(MaxReadingProperty)
End Sub

Panggilan balik nilai koersi memeriksa nilai properti yang mungkin bergantung pada properti saat ini, dan memaksa nilai saat ini jika perlu:

private static object CoerceCurrentReading(DependencyObject d, object value)
{
  Gauge g = (Gauge)d;
  double current = (double)value;
  if (current < g.MinReading) current = g.MinReading;
  if (current > g.MaxReading) current = g.MaxReading;
  return current;
}
Private Shared Function CoerceCurrentReading(ByVal d As DependencyObject, ByVal value As Object) As Object
    Dim g As Gauge = CType(d, Gauge)
    Dim current As Double = CDbl(value)
    If current < g.MinReading Then
        current = g.MinReading
    End If
    If current > g.MaxReading Then
        current = g.MaxReading
    End If
    Return current
End Function

Catatan

Nilai default properti tidak dipaksa. Nilai properti yang sama dengan nilai default mungkin terjadi jika nilai properti masih memiliki default awal, atau melalui menghapus nilai lain dengan ClearValue.

Nilai koersi dan panggilan balik yang diubah properti adalah bagian dari metadata properti. Oleh karena itu, Anda dapat mengubah panggilan balik untuk properti dependensi tertentu seperti yang ada pada jenis yang Anda dapatkan dari jenis yang memiliki properti dependensi, dengan mengambil alih metadata untuk properti tersebut pada jenis Anda.

Skenario Koersi dan Panggilan Balik Tingkat Lanjut

Batasan dan Nilai yang Diinginkan

Panggilan CoerceValueCallback balik akan digunakan oleh sistem properti untuk memaksa nilai sesuai dengan logika yang Anda nyatakan, tetapi nilai koerced dari properti yang ditetapkan secara lokal masih akan mempertahankan "nilai yang diinginkan" secara internal. Jika batasan didasarkan pada nilai properti lain yang dapat berubah secara dinamis selama masa pakai aplikasi, batasan koersi juga diubah secara dinamis, dan properti yang dibatasi dapat mengubah nilainya untuk mendekati nilai yang diinginkan sedekat mungkin mengingat batasan baru. Nilai akan menjadi nilai yang diinginkan jika semua batasan diangkat. Anda dapat berpotensi memperkenalkan beberapa skenario dependensi yang cukup rumit jika Anda memiliki beberapa properti yang bergantung satu sama lain dengan cara yang melingkar. Misalnya, dalam skenario Min/Max/Current, Anda dapat memilih agar Minimum dan Maksimum dapat diatur pengguna. Jika demikian, Anda mungkin perlu memaksakan bahwa Maksimum selalu lebih besar dari Minimum dan sebaliknya. Tetapi jika paksaan tersebut aktif, dan Maksimum memaksa ke Minimum, itu meninggalkan Arus dalam keadaan tidak dapat disetel, karena tergantung pada keduanya dan dibatasi pada rentang antara nilai, yaitu nol. Kemudian, jika Maksimum atau Minimum disesuaikan, Saat ini tampaknya akan "mengikuti" salah satu nilai, karena nilai yang diinginkan saat ini masih disimpan dan mencoba mencapai nilai yang diinginkan karena batasan dilonggarkan.

Secara teknis tidak ada yang salah dengan dependensi kompleks, tetapi mereka dapat sedikit merugikan performa jika membutuhkan sejumlah besar reevaluasi, dan juga dapat membingungkan pengguna jika mereka memengaruhi UI secara langsung. Berhati-hatilah dengan properti yang diubah dan koerce nilai panggilan balik dan pastikan bahwa paksaan yang dicoba dapat diperlakukan sebagai tidak ambigu mungkin, dan tidak "overconstrain".

Menggunakan CoerceValue untuk Membatalkan Perubahan Nilai

Sistem properti akan memperlakukan apa pun CoerceValueCallback yang mengembalikan nilai UnsetValue sebagai kasus khusus. Kasus khusus ini berarti bahwa perubahan properti yang mengakibatkan dipanggil harus ditolak CoerceValueCallback oleh sistem properti, dan bahwa sistem properti harus melaporkan nilai sebelumnya apa pun yang dimiliki properti. Mekanisme ini dapat berguna untuk memeriksa bahwa perubahan pada properti yang dimulai secara asinkron masih berlaku untuk status objek saat ini, dan menekan perubahan jika tidak. Skenario lain yang mungkin adalah Anda dapat secara selektif menekan nilai tergantung pada komponen penentuan nilai properti mana yang bertanggung jawab atas nilai yang dilaporkan. Untuk melakukan ini, Anda dapat menggunakan yang DependencyProperty diteruskan dalam panggilan balik dan pengidentifikasi properti sebagai input untuk GetValueSource, lalu memproses ValueSource.

Baca juga