Automatyzacja interfejsu użytkownika niestandardowej kontrolki WPF
Automatyzacja interfejsu użytkownika udostępnia jeden, uogólniony interfejs, którego klienci automatyzacji mogą używać do badania lub obsługiwania interfejsów użytkownika różnych platform i frameworków. Automatyzacja interfejsu użytkownika umożliwia korzystanie zarówno z kodu kontroli jakości (testu) jak i aplikacji ułatwień dostępu, takich jak czytniki zawartości ekranu w celu sprawdzenia elementów interfejsu użytkownika i symulowania interakcji użytkownika z nimi z innego kodu. Aby uzyskać informacje na temat automatyzacji interfejsu użytkownika na wszystkich platformach, zobacz Ułatwienia dostępu.
W tym temacie opisano sposób implementowania dostawcy automatyzacji interfejsu użytkownika po stronie serwera dla niestandardowej kontrolki uruchamianej w aplikacji WPF. WPF obsługuje automatyzację interfejsu użytkownika za pomocą drzewa obiektów automatyzacji równorzędnej, które są równoległe do drzewa elementów interfejsu użytkownika. Testy kodu i aplikacji, które oferują funkcje ułatwień dostępu, mogą bezpośrednio korzystać z obiektów automatyzacji (dla kodu wewnętrznego) lub za pośrednictwem uogólnionego interfejsu udostępnianego przez Automatyzację Interfejsu Użytkownika.
Klasy równorzędne automatyzacji
Kontrolki WPF obsługują automatyzację interfejsu użytkownika za pomocą drzewa klas równorzędnych, które pochodzą z AutomationPeer. Zgodnie z konwencją nazwy klas równorzędnych zaczynają się od nazwy klasy sterującej i kończą się ciągiem "AutomationPeer". Na przykład ButtonAutomationPeer jest klasą równorzędną klasy kontrolki Button. Klasy równorzędne z grubsza odpowiadają typom kontrolek automatyzacji UI, ale są specyficzne dla elementów WPF. Kod automatyzacji, który uzyskuje dostęp do aplikacji WPF za pośrednictwem interfejsu automatyzacji UI, nie korzysta bezpośrednio z elementów wspomagających automatyzację, ale kod automatyzacji w tej samej przestrzeni procesowej może korzystać z nich bezpośrednio.
Wbudowane klasy automatyzacji Peer
Elementy implementują klasę równorzędną automatyzacji, jeśli akceptują działanie interfejsu od użytkownika lub zawierają informacje wymagane przez użytkowników aplikacji czytnika zawartości ekranu. Nie wszystkie elementy wizualne WPF mają partnerów automatyzacji. Przykłady klas implementujących równorzędne automatyzacji to Button, TextBoxi Label. Przykłady klas, które nie implementują elementów równorzędnych automatyzacji, to klasy, które pochodzą z Decorator, takie jak Border, i to również klasy oparte na Panel, takie jak Grid i Canvas.
Klasa podstawowa Control nie ma odpowiedniej klasy równorzędnej. Jeśli potrzebujesz klasy równorzędnej odpowiadającej kontrolce niestandardowej pochodzącej z Control, należy utworzyć niestandardową klasę równorzędną z FrameworkElementAutomationPeer.
Zagadnienia dotyczące zabezpieczeń dla pochodnych elementów równorzędnych
Elementy równorzędne automatyzacji muszą działać w środowisku częściowo zaufanym. Kod w zestawie UIAutomationClient nie jest skonfigurowany do uruchamiania w środowisku o częściowym zaufaniu, a kod elementu równorzędnego automatyzacji nie powinien odwoływać się do tego zestawu. Zamiast tego należy użyć klas w zestawie UIAutomationTypes. Na przykład należy użyć klasy AutomationElementIdentifiers z zestawu UIAutomationTypes, który odpowiada klasie AutomationElement w zestawie UIAutomationClient. Można bezpiecznie odwołać się do zestawu UIAutomationTypes w kodzie automatyzacji peer.
Nawigacja poprzez rówieśników
Po zlokalizowaniu równorzędnego elementu automatyzacji, kod procesu może nawigować po drzewie równorzędnym, wywołując metody GetChildren i GetParent obiektu. Nawigacja między elementami WPF w kontrolce jest obsługiwana przez implementację metody GetChildrenCore elementu równorzędnego. System automatyzacji interfejsu użytkownika wywołuje tę metodę w celu utworzenia drzewa podelementów zawartych w kontrolce; na przykład elementy listy w polu listy. Domyślna metoda UIElementAutomationPeer.GetChildrenCore przechodzi przez drzewo wizualne elementów w celu utworzenia drzewa elementów partnerskich dla automatyzacji. Kontrolki niestandardowe zastępują tę metodę, aby uwidocznić elementy podrzędne klientom automatyzacji, zwracając elementy automatyzacji, które przekazują informacje lub umożliwiają użytkownikowi interakcję.
Dostosowania w pochodnym elemencie równorzędnym
Wszystkie klasy pochodzące z UIElement i ContentElement zawierają chronioną metodę wirtualną OnCreateAutomationPeer. WPF wywołuje OnCreateAutomationPeer, aby uzyskać obiekt automatyzacji równorzędnej dla każdej kontrolki. Kod automatyzacji może używać obiektu równorzędnego do uzyskiwania informacji o charakterystykach i funkcjach kontrolki oraz do symulowania interakcji. Niestandardowa kontrolka obsługująca automatyzację musi zastąpić OnCreateAutomationPeer i zwrócić wystąpienie klasy pochodzącej z AutomationPeer. Jeśli na przykład kontrolka niestandardowa pochodzi z klasy ButtonBase, obiekt zwrócony przez OnCreateAutomationPeer powinien pochodzić z ButtonBaseAutomationPeer.
Podczas implementowania kontrolki niestandardowej należy przesłonić metody "Core" z podstawowej klasy automatyzacji peer, które opisują zachowanie unikatowe i specyficzne dla niestandardowej kontrolki.
Zastąpij metodę OnCreateAutomationPeer
Zastąpi metodę OnCreateAutomationPeer dla kontrolki niestandardowej, aby zwracała obiekt dostawcy, który musi pochodzić bezpośrednio lub pośrednio z AutomationPeer.
Zastąp metodę GetPattern
Równe elementy automatyzacji upraszczają niektóre aspekty implementacji dostawców automatyzacji interfejsu użytkownika po stronie serwera, ale elementy automatyzacji niestandardowej kontroli muszą nadal obsługiwać interfejsy wzorca. Podobnie jak dostawcy nienależący do WPF, elementy partnerskie obsługują wzorce kontrolne, zapewniając implementacje interfejsów w przestrzeni nazw System.Windows.Automation.Provider, takie jak IInvokeProvider. Interfejsy wzorca sterowania mogą być zaimplementowane przez sam obiekt równorzędny lub inny obiekt. Implementacja GetPattern równorzędnika zwraca obiekt, który obsługuje określony wzorzec. Kod automatyzacji interfejsu użytkownika wywołuje metodę GetPattern i określa wartość wyliczenia PatternInterface. Zastąpienie GetPattern powinno zwrócić obiekt implementujący określony wzorzec. Jeśli kontrolka nie ma niestandardowej implementacji wzorca, możesz wywołać implementację podstawowego typu GetPattern, aby pobrać jego implementację lub wartość null, jeśli wzorzec nie jest obsługiwany dla tego typu kontrolki. Na przykład kontrolkę niestandardową NumericUpDown można ustawić na wartość w określonym zakresie, a jej rówieśnicy w automatyzacji interfejsu użytkownika implementowaliby interfejs IRangeValueProvider. W poniższym przykładzie pokazano, jak metoda GetPattern elementu równorzędnego jest zastępowana w celu reagowania na wartość 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
Metoda GetPattern może również określać podelement jako dostawcę wzorca. Poniższy kod pokazuje, jak ItemsControl przekazuje obsługę wzorca przewijania do elementu równorzędnego kontrolki wewnętrznej 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
Aby określić podelement obsługi wzorca, ten kod pobiera obiekt podelementu, tworzy element równorzędny przy użyciu metody CreatePeerForElement, ustawia właściwość EventsSource nowego elementu równorzędnego do bieżącego elementu równorzędnego i zwraca nowy element równorzędny. Ustawienie EventsSource w podelemencie uniemożliwia wyświetlenie podelementu w drzewie elementów automatyzacji równorzędnej i wyznacza wszystkie zdarzenia zgłaszane przez podelement jako pochodzące z kontroli określonej w EventsSource. Kontrolka ScrollViewer nie jest wyświetlana w drzewie automatyzacji, a zdarzenia przewijania, które generuje, wydają się pochodzić z obiektu ItemsControl.
Nadpisywanie metod "Core"
Kod automatyzacji pobiera informacje o kontrolce, wywołując publiczne metody klasy równorzędnej. Aby podać informacje o kontrolce, zastąpij każdą metodę, której nazwa kończy się ciągiem "Core", gdy implementacja kontrolki różni się od tej udostępnionej przez klasę równorzędną automatyzacji podstawowej. Co najmniej kontrolka musi implementować metody GetClassNameCore i GetAutomationControlTypeCore, jak pokazano w poniższym przykładzie.
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
Implementacja GetAutomationControlTypeCore opisuje kontrolę, zwracając wartość ControlType. Chociaż można zwrócić ControlType.Custom, należy zwrócić jeden z bardziej szczegółowych typów kontrolek, jeśli dokładnie opisuje Twój element sterujący. Wartość zwracana ControlType.Custom wymaga dodatkowej pracy dla dostawcy w celu zaimplementowania automatyzacji interfejsu użytkownika, a produkty klienckie automatyzacji interfejsu użytkownika nie mogą przewidzieć struktury sterowania, interakcji z klawiaturą i możliwych wzorców sterowania.
Zaimplementuj metody IsContentElementCore i IsControlElementCore, aby wskazać, czy kontrolka zawiera zawartość danych, czy spełnia interaktywną rolę w interfejsie użytkownika (lub oba te metody). Domyślnie obie metody zwracają true
. Te ustawienia zwiększają użyteczność narzędzi automatyzacji, takich jak czytniki zawartości ekranu, które mogą używać tych metod do filtrowania drzewa automatyzacji. Jeśli metoda GetPattern przenosi obsługę wzorca do równorzędnego podelementu, metoda IsControlElementCore tego równorzędnego podelementu może zwrócić wartość false, aby ukryć go z drzewa automatyzacji. Na przykład przewijanie w ListBox jest obsługiwane przez ScrollViewer, a element równorzędny automatyzacji dla PatternInterface.Scroll jest zwracany przez metodę GetPattern klasy ScrollViewerAutomationPeer, skojarzoną z ListBoxAutomationPeer. W związku z tym metoda IsControlElementCore klasy ScrollViewerAutomationPeer zwraca wartość false
, tak że ScrollViewerAutomationPeer nie pojawia się w drzewie automatyzacji.
Twój partner automatyzacji powinien zapewnić odpowiednie wartości domyślne dla kontrolki. Należy pamiętać, że język XAML, który odwołuje się do kontrolki, może zastąpić implementacje równorzędne metod podstawowych przez uwzględnienie atrybutów AutomationProperties. Na przykład poniższy kod XAML tworzy przycisk z dwoma dostosowanymi właściwościami automatyzacji interfejsu użytkownika.
<Button AutomationProperties.Name="Special"
AutomationProperties.HelpText="This is a special button."/>
Implementacja dostawców wzorców
Interfejsy implementowane przez dostawcę niestandardowego są jawnie deklarowane, jeśli element właścicielski pochodzi bezpośrednio z Control. Na przykład poniższy kod deklaruje element równorzędny dla Control, który implementuje wartość zakresu.
public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }
Public Class RangePeer1
Inherits FrameworkElementAutomationPeer
Implements IRangeValueProvider
End Class
Jeśli kontrolka będąca właścicielem pochodzi z określonego typu kontrolki, takiego jak RangeBase, element równorzędny może pochodzić z równoważnej klasy równorzędnej pochodnej. W tym przypadku odpowiednik jest generowany z RangeBaseAutomationPeer, który dostarcza podstawową implementację IRangeValueProvider. Poniższy kod przedstawia deklarację takiego elementu równorzędnego.
public class RangePeer2 : RangeBaseAutomationPeer { }
Public Class RangePeer2
Inherits RangeBaseAutomationPeer
End Class
Aby zapoznać się z przykładową implementacją, zobacz kod źródłowy języka C# lub Visual Basic, który implementuje i używa niestandardowej kontrolki NumericUpDown.
Wywoływanie zdarzeń
Klienci usługi Automation mogą subskrybować zdarzenia automatyzacji. Kontrolki niestandardowe muszą zgłaszać zmiany stanu kontroli przez wywołanie metody RaiseAutomationEvent. Podobnie, gdy wartość właściwości zmieni się, wywołaj metodę RaisePropertyChangedEvent. Poniższy kod pokazuje, jak uzyskać obiekt elementu równorzędnego z poziomu kodu sterującego i wywołać metodę w celu wywołania zdarzenia. W ramach optymalizacji kod określa, czy istnieją odbiorniki dla tego typu zdarzenia. Wywołanie zdarzenia tylko wtedy, gdy są słuchacze, unika niepotrzebnego obciążenia i pomaga w utrzymaniu responsywności kontrolki.
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
Zobacz też
- Omówienie automatyzacji interfejsu użytkownika
- Server-Side Implementacja dostawcy automatyzacji interfejsu użytkownika
- kontrolka niestandardowa NumericUpDown (C#) w witrynie GitHub
- niestandardowa kontrolka NumericUpDown (Visual Basic) w witrynie GitHub
.NET Desktop feedback