Bagikan melalui


Otomatisasi UI Kontrol Kustom WPF

Automasi UI menyediakan satu antarmuka umum yang dapat digunakan klien otomatisasi untuk memeriksa atau mengoperasikan antarmuka pengguna dari berbagai platform dan kerangka kerja. Automasi UI memungkinkan kode jaminan kualitas (pengujian) dan aplikasi aksesibilitas seperti pembaca layar untuk memeriksa elemen antarmuka pengguna dan mensimulasikan interaksi pengguna dengan mereka dari kode lain. Untuk informasi tentang Automasi UI di semua platform, lihat Aksesibilitas.

Topik ini menjelaskan cara mengimplementasikan penyedia Automation UI sisi server untuk kontrol kustom yang berjalan dalam aplikasi WPF. WPF mendukung Automasi UI melalui pohon objek otomatisasi rekan yang sejajar dengan pohon elemen antarmuka pengguna. Kode pengujian dan aplikasi yang menyediakan fitur aksesibilitas dapat menggunakan objek peer otomatisasi secara langsung (untuk kode dalam proses) atau melalui antarmuka umum yang disediakan oleh UI Automation.

Kelas Peer Otomasi

Kontrol WPF mendukung Automasi UI melalui pohon kelas rekan yang berasal dari AutomationPeer. Menurut konvensi, nama kelas rekan dimulai dengan nama kelas kontrol dan diakhiri dengan "AutomationPeer." Misalnya, ButtonAutomationPeer adalah kelas rekan untuk kelas kontrol Button. Kelas rekan kira-kira setara dengan jenis kontrol UI Automation tetapi khusus untuk elemen WPF. Kode automasi yang mengakses aplikasi WPF melalui antarmuka Automation UI tidak menggunakan rekan otomatisasi secara langsung, tetapi kode otomatisasi di ruang proses yang sama dapat menggunakan rekan otomatisasi secara langsung.

Kelas Rekan Otomasi Bawaan

Elemen menerapkan kelas peer otomatisasi jika mereka menerima aktivitas antarmuka dari pengguna, atau jika berisi informasi yang diperlukan oleh pengguna aplikasi pembaca layar. Tidak semua elemen visual WPF memiliki komponen otomatisasi. Contoh kelas yang menerapkan rekan otomatisasi adalah Button, , TextBoxdan Label. Contoh kelas yang tidak menerapkan rekan otomatisasi adalah kelas yang berasal dari Decorator, seperti Border, dan kelas berdasarkan Panel, seperti Grid dan Canvas.

Kelas dasar Control tidak memiliki kelas serekan yang sesuai. Jika Anda memerlukan kelas serekan yang sesuai dengan kontrol kustom yang berasal dari Control, Anda harus memperoleh kelas serekan kustom dari FrameworkElementAutomationPeer.

Pertimbangan Keamanan untuk Rekan Turunan

Rekan automasi harus berjalan di lingkungan kepercayaan parsial. Kode dalam rakitan UIAutomationClient tidak dikonfigurasi untuk berjalan di lingkungan kepercayaan parsial, dan kode serekan otomatisasi tidak boleh mereferensikan perakitan tersebut. Sebagai gantinya, Anda harus menggunakan kelas di rakitan UIAutomationTypes. Misalnya, Anda harus menggunakan AutomationElementIdentifiers kelas dari rakitan UIAutomationTypes, yang sesuai dengan AutomationElement kelas di rakitan UIAutomationClient. Aman untuk mereferensikan assembly UIAutomationTypes dalam kode sejawat otomatisasi.

Navigasi Serekan

Setelah menemukan rekan otomatisasi, kode yang sedang berjalan dapat menavigasi pohon rekan dengan memanggil metode GetChildren dan GetParent objek. Navigasi di antara elemen WPF dalam kontrol didukung oleh implementasi metode peer GetChildrenCore. Sistem Automasi UI memanggil metode ini untuk membangun pohon subelemen yang terkandung dalam kontrol; misalnya, mencantumkan item dalam kotak daftar. Metode default UIElementAutomationPeer.GetChildrenCore melintasi pohon visual elemen untuk membangun pohon rekan otomatisasi. Kontrol kustom mengambil alih metode ini untuk mengekspos elemen turunan ke klien otomatisasi, mengembalikan serekan otomatisasi elemen yang menyampaikan informasi atau mengizinkan interaksi pengguna.

Kustomisasi pada Rekan Turunan

Semua kelas yang berasal dari UIElement dan ContentElement berisi metode OnCreateAutomationPeervirtual yang dilindungi . WPF memanggil OnCreateAutomationPeer untuk mendapatkan objek mitra otomatisasi untuk setiap kontrol. Kode automasi dapat menggunakan rekan untuk mendapatkan informasi tentang karakteristik dan fitur kendali dan untuk menyimulasikan penggunaan secara interaktif. Kontrol kustom yang mendukung otomatisasi harus mengambil alih OnCreateAutomationPeer dan mengembalikan instans kelas yang berasal dari AutomationPeer. Misalnya, jika kontrol kustom berasal dari ButtonBase kelas , maka objek yang dikembalikan oleh OnCreateAutomationPeer harus berasal dari ButtonBaseAutomationPeer.

Saat menerapkan kontrol kustom, Anda harus mengambil alih metode "Core" dari kelas peer otomatisasi dasar yang menggambarkan perilaku unik dan khusus untuk kontrol kustom Anda.

Mengambil alih OnCreateAutomationPeer

Override metode OnCreateAutomationPeer untuk kontrol kustom Anda agar dapat mengembalikan objek penyedia Anda, yang harus diturunkan langsung atau tidak langsung dari AutomationPeer.

Mengambil alih GetPattern

Peer Automation menyederhanakan beberapa aspek implementasi penyedia Automation UI sisi server, tetapi rekan otomatisasi kontrol kustom masih harus menangani antarmuka pola. Seperti provider non-WPF, rekan mendukung pola kontrol dengan menyediakan implementasi antarmuka dalam namespace, seperti antarmuka System.Windows.Automation.Provider. Antarmuka pola kontrol dapat diimplementasikan oleh rekan itu sendiri atau oleh objek lain. Implementasi sebaya GetPattern mengembalikan objek yang mendukung pola tertentu. Kode Automasi UI memanggil GetPattern metode dan menentukan PatternInterface nilai enumerasi. Penimpaan GetPattern Anda harus mengembalikan objek yang mengimplementasikan pola yang ditentukan. Jika kontrol Anda tidak memiliki implementasi kustom untuk pola, Anda dapat memanggil implementasi pola dari jenis dasar untuk mendapatkan implementasinya, atau null jika pola tersebut tidak didukung untuk jenis kontrol ini. Misalnya, kontrol NumericUpDown kustom dapat diatur ke nilai dalam rentang, sehingga peer Automation UI-nya akan mengimplementasikan IRangeValueProvider antarmuka. Contoh berikut menunjukkan bagaimana metode peer GetPattern diubah untuk merespons nilai PatternInterface.RangeValue.

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    return base.GetPattern(patternInterface);
}
Public Overrides Function GetPattern(ByVal patternInterface As PatternInterface) As Object
    If patternInterface = PatternInterface.RangeValue Then
        Return Me
    End If
    Return MyBase.GetPattern(patternInterface)
End Function

Metode GetPattern juga dapat menentukan subelemen sebagai penyedia pola. Kode berikut menunjukkan cara ItemsControl mentransfer penanganan pola gulir ke rekan dari kontrol internal miliknya ScrollViewer.

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.Scroll)
    {
        ItemsControl owner = (ItemsControl) base.Owner;

        // ScrollHost is internal to the ItemsControl class
        if (owner.ScrollHost != null)
        {
            AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost);
            if ((peer != null) && (peer is IScrollProvider))
            {
                peer.EventsSource = this;
                return (IScrollProvider) peer;
            }
        }
    }
    return base.GetPattern(patternInterface);
}
Public Class Class1
    Public Overrides Function GetPattern(ByVal patternInterface__1 As PatternInterface) As Object
        If patternInterface1 = PatternInterface.Scroll Then
            Dim owner As ItemsControl = DirectCast(MyBase.Owner, ItemsControl)

            ' ScrollHost is internal to the ItemsControl class
            If owner.ScrollHost IsNot Nothing Then
                Dim peer As AutomationPeer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost)
                If (peer IsNot Nothing) AndAlso (TypeOf peer Is IScrollProvider) Then
                    peer.EventsSource = Me
                    Return DirectCast(peer, IScrollProvider)
                End If
            End If
        End If
        Return MyBase.GetPattern(patternInterface1)
    End Function
End Class

Untuk menentukan subelemen untuk penanganan pola, kode ini mendapatkan obyek subelemen, membuat rekan dengan menggunakan metode CreatePeerForElement, mengatur properti EventsSource dari rekan baru ke rekan saat ini, dan mengembalikan rekan baru. Pengaturan EventsSource pada subelemen mencegah subelemen muncul di pohon peer otomatisasi dan menunjuk semua peristiwa yang dipicu oleh subelemen sebagai berasal dari kontrol yang ditentukan dalam EventsSource. ScrollViewer Kontrol tidak muncul di pohon otomatisasi, dan peristiwa pengguliran yang dihasilkannya tampaknya berasal dari objek ItemsControl.

Menimpa Metode "Core"

Kode automasi mendapatkan informasi tentang kontrol Anda dengan memanggil metode publik kelas serekan. Untuk memberikan informasi tentang kontrol Anda, ambil alih setiap metode yang namanya berakhiran dengan "Core" ketika implementasi kontrol Anda berbeda dari yang disediakan oleh kelas peer otomatisasi dasar. Setidaknya, kontrol Anda harus menerapkan metode GetClassNameCore dan GetAutomationControlTypeCore, seperti yang ditunjukkan dalam contoh berikut.

protected override string GetClassNameCore()
{
    return "NumericUpDown";
}

protected override AutomationControlType GetAutomationControlTypeCore()
{
    return AutomationControlType.Spinner;
}
Protected Overrides Function GetClassNameCore() As String
    Return "NumericUpDown"
End Function

Protected Overrides Function GetAutomationControlTypeCore() As AutomationControlType
    Return AutomationControlType.Spinner
End Function

Implementasi GetAutomationControlTypeCore Anda menjelaskan kontrol Anda dengan mengembalikan ControlType nilai. Meskipun Anda dapat mengembalikan ControlType.Custom, Anda harus mengembalikan salah satu jenis kontrol yang lebih spesifik jika secara akurat menjelaskan kontrol Anda. Nilai pengembalian ControlType.Custom memerlukan pekerjaan ekstra bagi penyedia untuk menerapkan Automasi UI, dan produk klien Automasi UI tidak dapat memperkirakan struktur kontrol, interaksi keyboard, dan kemungkinan pola kontrol.

Terapkan IsContentElementCore metode dan IsControlElementCore untuk menunjukkan apakah kontrol Anda berisi konten data atau memenuhi peran interaktif di antarmuka pengguna (atau keduanya). Secara default, kedua metode mengembalikan true. Pengaturan ini meningkatkan kegunaan alat otomatisasi seperti pembaca layar, yang dapat menggunakan metode ini untuk memfilter pohon otomatisasi. Jika metode GetPattern Anda mentransfer penanganan pola ke rekan subelemen, metode rekan subelemen IsControlElementCore dapat mengembalikan false untuk menyembunyikan rekan subelemen dari pohon otomatisasi. Misalnya, pengguliran dalam ListBox ditangani oleh ScrollViewer, dan peer otomatisasi untuk PatternInterface.Scroll dikembalikan oleh metode GetPattern dari ScrollViewerAutomationPeer yang terkait dengan ListBoxAutomationPeer. Oleh karena itu, metode IsControlElementCore dari ScrollViewerAutomationPeer mengembalikan false, sehingga ScrollViewerAutomationPeer tidak muncul di pohon otomatisasi.

Peer otomatisasi Anda harus menyediakan nilai default yang tepat untuk pengendalian Anda. Perhatikan bahwa XAML yang mereferensikan kontrol Anda dapat menggantikan implementasi setara fungsi utama Anda dengan menyertakan atribut AutomationProperties. Misalnya, XAML berikut membuat tombol yang memiliki dua properti UI Automation yang disesuaikan.

<Button AutomationProperties.Name="Special"
    AutomationProperties.HelpText="This is a special button."/>

Menerapkan Penyedia Pola

Antarmuka yang diterapkan oleh penyedia kustom secara eksplisit dideklarasikan jika elemen pemilik berasal langsung dari Control. ** Misalnya, kode berikut mendeklarasikan rekan untuk Control yang mengimplementasikan nilai rentang.

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }
Public Class RangePeer1
    Inherits FrameworkElementAutomationPeer
    Implements IRangeValueProvider
End Class

Jika kontrol pemilik berasal dari jenis kontrol tertentu seperti RangeBase, rekan dapat berasal dari kelas serekan turunan yang setara. Dalam hal ini, rekan akan berasal dari RangeBaseAutomationPeer, yang memasok implementasi dasar IRangeValueProvider. Kode berikut menunjukkan deklarasi mitra tersebut.

public class RangePeer2 : RangeBaseAutomationPeer { }
Public Class RangePeer2
    Inherits RangeBaseAutomationPeer
End Class

Untuk contoh implementasi, lihat kode sumber C# atau Visual Basic yang mengimplementasikan dan menggunakan kontrol kustom NumericUpDown.

Memicu Peristiwa

Klien Automation dapat berlangganan peristiwa otomatisasi. Kontrol kustom harus melaporkan perubahan ke status kontrol dengan memanggil RaiseAutomationEvent metode . Demikian pula, ketika nilai properti berubah, panggil RaisePropertyChangedEvent metode . Kode berikut menunjukkan cara mendapatkan objek sebaya dari dalam kode kontrol dan memanggil metode untuk memicu sebuah peristiwa. Sebagai pengoptimalan, kode menentukan apakah ada pendengar untuk jenis peristiwa ini. Meningkatkan peristiwa hanya ketika ada pendengar menghindari overhead yang tidak perlu dan membantu kontrol tetap responsif.

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer =
        UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;

    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}
If AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged) Then
    Dim peer As NumericUpDownAutomationPeer = TryCast(UIElementAutomationPeer.FromElement(nudCtrl), NumericUpDownAutomationPeer)

    If peer IsNot Nothing Then
        peer.RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty, CDbl(oldValue), CDbl(newValue))
    End If
End If

Lihat juga