WPF カスタム コントロールの UI オートメーション
更新 : 2008 年 7 月
Microsoft UI オートメーションは、オートメーション クライアントが各種のプラットフォームおよびフレームワークのユーザー インターフェイスを確認または操作するときに使用できる、単一の汎用的なインターフェイスを提供します。UI オートメーションによって、品質保証 (テスト) のためのコードと、スクリーン リーダーなどのユーザー補助アプリケーションの両方で、ユーザー インターフェイス要素をチェックし、ユーザーによる別のコードからのそれらの要素の操作をシミュレーションできます。各種プラットフォームでの UI オートメーションの詳細については、「ユーザー補助」を参照してください。
ここでは、WPF アプリケーションで実行されるカスタム コントロール用にサーバー側の UI オートメーション プロバイダを実装する方法について説明します。WPF では、ユーザー インターフェイス要素のツリーに対応するピア オートメーション オブジェクトのツリーを使用して、UI オートメーションをサポートします。テスト コードや、ユーザー補助機能を提供するアプリケーションは、ピア オブジェクトを直接使用することも (インプロセス コードの場合)、UI オートメーションが提供する汎用のインターフェイスをとおして使用することもできます。
このトピックには次のセクションが含まれています。
- オートメーション ピア クラス
- 組み込みのオートメーション ピア クラス
- 派生ピアのセキュリティに関する考慮事項
- ピアのナビゲーション
- 派生ピアでのカスタマイズ
- 関連トピック
オートメーション ピア クラス
WPF では、AutomationPeer から派生するピア クラスのツリーを使用して、UI オートメーションをサポートします。規則により、ピア クラスの名前はコントロール クラスの名前で始まり "AutomationPeer" で終わります。たとえば、ButtonAutomationPeer は Button コントロール クラスのピア クラスです。ピア クラスは UI オートメーション コントロール型とほぼ同等ですが、WPF 要素に固有のものです。UI オートメーション インターフェイスを使用して WPF アプリケーションにアクセスするオートメーション コードはオートメーション ピアを直接には使用しませんが、同じプロセス空間内のオートメーション コードはオートメーション ピアを直接使用できます。
組み込みのオートメーション ピア クラス
要素がユーザーによるインターフェイス アクティビティを受け入れる場合、または要素にスクリーンリーダー アプリケーションのユーザーが必要とする情報が含まれている場合、それらの要素はオートメーション ピア クラスを実装します。WPF のすべてのビジュアル要素がオートメーション ピアを持つわけではありません。オートメーション ピアを実装するクラスの例としては、Button、TextBox、および Label があります。オートメーション ピアを実装しないクラスの例としては、Decorator から派生するクラス (Border など)、および Panel に基づくクラス (Grid、Canvas など) があります。
Control 基本クラスには、対応するピア クラスはありません。Control から派生するカスタム コントロールのピア クラスが必要な場合は、FrameworkElementAutomationPeer からカスタム ピア クラスを派生させる必要があります。
派生ピアのセキュリティに関する考慮事項
オートメーション ピアは、部分信頼環境で実行する必要があります。UIAutomationClient アセンブリ内のコードは部分信頼環境で実行されるように構成されておらず、オートメーション ピア コードはこのアセンブリを参照しないようにする必要があります。その代わりに、UIAutomationTypes アセンブリ内のクラスを使用します。たとえば、UIAutomationTypes アセンブリ内の AutomationElementIdentifiers クラスを使用します。このクラスは、UIAutomationClient アセンブリ内の AutomationElement クラスに対応します。オートメーション ピア コードで UIAutomationTypes アセンブリを参照しても問題ありません。
ピアのナビゲーション
オートメーション ピアが見つかると、インプロセス コードはオブジェクトの GetChildren メソッドと GetParent メソッドを呼び出して、ピア ツリーをナビゲーションすることができます。コントロール内の WPF 要素間のナビゲーションは、ピアの GetChildrenCore メソッドの実装によってサポートされます。UI オートメーション システムは、このメソッドを呼び出して、コントロール内のサブ要素 (リスト ボックス内のリスト項目など) のツリーを作成します。既定の UIElementAutomationPeer.GetChildrenCore メソッドは、要素のビジュアル ツリーを移動して、オートメーション ピアのツリーを作成します。カスタム コントロールはこのメソッドをオーバーライドして子要素をオートメーション クライアントに公開し、情報を伝達する要素やユーザー操作を許可する要素のオートメーション ピアを返します。
派生ピアでのカスタマイズ
UIElement および ContentElement から派生するすべてのクラスには、プロテクト仮想メソッド OnCreateAutomationPeer が含まれています。WPF は OnCreateAutomationPeer を呼び出して、各コントロールのオートメーション ピア オブジェクトを取得します。オートメーション コードはピアを使用して、コントロールの特性と機能に関する情報を取得したり、対話型の使用をシミュレーションしたりすることができます。オートメーションをサポートするカスタム コントロールは、OnCreateAutomationPeer をオーバーライドし、AutomationPeer から派生するクラスのインスタンスを返す必要があります。たとえば、ButtonBase クラスから派生するカスタム コントロールの場合、OnCreateAutomationPeer で返されるオブジェクトは ButtonBaseAutomationPeer から派生したものであることが必要です。
カスタム コントロールを実装するときには、そのカスタム コントロールに固有な一意の動作を表す、基本オートメーション ピア クラスの "Core" メソッドをオーバーライドする必要があります。
OnCreateAutomationPeer のオーバーライド
カスタム コントロールの OnCreateAutomationPeer メソッドをオーバーライドして、AutomationPeer から直接的または間接的に派生するプロバイダ オブジェクトを返すようにします。
GetPattern のオーバーライド
オートメーション ピアによって、サーバー側 UI オートメーション プロバイダの実装の一部が簡略化されますが、カスタム コントロール オートメーション ピアでパターン インターフェイスを処理する必要性は変わりません。ピアは、非 WPF プロバイダと同様に、System.Windows.Automation.Provider 名前空間内のインターフェイス (IInvokeProvider など) の実装を提供することによってコントロール パターンをサポートします。コントロール パターン インターフェイスは、ピア自体または別のオブジェクトによって実装できます。ピアの GetPattern の実装は、指定されたパターンをサポートするオブジェクトを返します。UI オートメーション コードは、GetPattern メソッドを呼び出し、PatternInterface 列挙値を指定します。GetPattern のオーバーライドでは、指定されたパターンを実装するオブジェクトが返される必要があります。パターンのカスタム実装がコントロールに含まれていない場合、基本型の GetPattern の実装を呼び出すことで、その実装、または null (このコントロール型でパターンがサポートされていない場合) を取得できます。たとえば、カスタムの NumericUpDown コントロールを範囲内の値に設定して、その UI オートメーション ピアが IRangeValueProvider インターフェイスを実装するようにすることができます。次の例は、ピアの GetPattern メソッドを PatternInterface.RangeValue 値に応答するようにオーバーライドする方法を示しています。
public override object GetPattern(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.RangeValue)
{
return this;
}
return base.GetPattern(patternInterface);
}
GetPattern メソッドでは、サブ要素をパターン プロバイダとして指定することもできます。次のコードは、ItemsControl がその内部 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);
}
パターン処理用にサブ要素を指定するため、このコードはサブ要素オブジェクトを取得し、CreatePeerForElement メソッドを使用してピアを作成し、新しいピアの EventsSource プロパティを現在のピアに設定し、新しいピアを返します。サブ要素に EventsSource を設定すると、そのサブ要素はオートメーション ピア ツリーに表示されず、そのサブ要素が発生させるすべてのイベントは EventsSource で指定したコントロールから生成されたものとして扱われます。ScrollViewer コントロールはオートメーション ツリーに表示されず、それが生成するスクロール イベントは ItemsControl オブジェクトから生成されたものとして表示されます。
"Core" メソッドのオーバーライド
オートメーション コードは、ピア クラスのパブリック メソッドを呼び出して、コントロールに関する情報を取得します。コントロールに関する情報を提供するためには、コントロールの実装が基本オートメーション ピア クラスが提供する実装と異なる場合に、"Core" で終わる各メソッドをオーバーライドします。次の例で示すように、コントロールが少なくとも GetClassNameCore メソッドと GetAutomationControlTypeCore メソッドを実装している必要があります。
protected override string GetClassNameCore()
{
return "NumericUpDown";
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Spinner;
}
GetAutomationControlTypeCore の実装は、ControlType 値を返すことによって、コントロールを表します。ControlType.Custom を返すことはできますが、コントロールを正確に表す場合は、より限定的なコントロール型のいずれかを返す必要があります。ControlType.Custom の戻り値では、プロバイダが UI オートメーションを実装するのに余分な作業が必要となり、UI オートメーションのクライアント製品は、制御構造、キーボード操作、およびコントロール パターンを予測できません。
コントロールにデータ コンテンツが含まれるか、コントロールがユーザー インターフェイスで対話的な役割を果たすか、またはその両方であることを示すには、IsContentElementCore メソッドと IsControlElementCore メソッドを実装します。既定では、両方のメソッドが true を返します。これらの設定によって、オートメーション ツリーのフィルタ処理にこれらを使用する、スクリーン リーダーなどのオートメーション ツールの操作性が向上します。GetPattern メソッドがパターン処理をサブ要素のピアに転送する場合、サブ要素のピアの IsControlElementCore メソッドは、false を返してオートメーション ツリーにサブ要素のピアが表示されないようにすることができます。たとえば、ListBox でのスクロールは ScrollViewer によって処理され、PatternInterface.Scroll のオートメーション ピアは、ListBoxAutomationPeer と関連付けられている ScrollViewerAutomationPeer の GetPattern メソッドから返されます。このため ScrollViewerAutomationPeer の IsControlElementCore メソッドは false を返し、ScrollViewerAutomationPeer はオートメーション ツリーに表示されません。
オートメーション ピアは、コントロール用に適切な既定値を提供する必要があります。コントロールを参照する XAML は、AutomationProperties 属性を含めることによって、"Core" メソッドのピア実装をオーバーライドできます。たとえば、次の XAML では、2 つのカスタマイズされた UI オートメーション プロパティを持つボタンを作成します。
<Button AutomationProperties.Name="Special"
AutomationProperties.HelpText="This is a special button."/>
パターン プロバイダの実装
所有する要素が Control から直接派生する場合は、カスタム プロバイダによって実装されたインターフェイスを明示的に宣言します。たとえば、次のコードでは、範囲値を実装する Control のピアを宣言しています。
public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }
所有するコントロールが特定のコントロール型 (RangeBase など) から派生する場合は、それと等価な派生ピア クラスからピアを派生させることができます。この場合は、IRangeValueProvider の基本実装を提供する RangeBaseAutomationPeer からピアを派生させます。次のコードでは、こうしたピアを宣言しています。
public class RangePeer2 : RangeBaseAutomationPeer { }
実装例については、「テーマおよび UI オートメーションがサポートされた NumericUpDown カスタム コントロールのサンプル」を参照してください。
イベントの発生
オートメーション クライアントはオートメーション イベントをサブスクライブできます。カスタム コントロールは、RaiseAutomationEvent メソッドを呼び出して、コントロールの状態に対する変更を報告する必要があります。同様に、プロパティ値が変更された場合は、RaisePropertyChangedEvent メソッドを呼び出します。次のコードは、コントロール コード内からピア オブジェクトを取得し、イベントを発生させるメソッドを呼び出す方法を示しています。最適化の手法として、このコードはこの種類のイベントのリスナが存在するかどうかを判断します。リスナが存在する場合にだけイベントを発生させることによって、不要なオーバーヘッドを回避し、コントロールの応答性の維持に役立てます。
if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
NumericUpDownAutomationPeer peer =
UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;
if (peer != null)
{
peer.RaisePropertyChangedEvent(
RangeValuePatternIdentifiers.ValueProperty,
(double)oldValue,
(double)newValue);
}
}
参照
処理手順
テーマおよび UI オートメーションがサポートされた NumericUpDown カスタム コントロールのサンプル
概念
履歴の変更
日付 |
履歴 |
理由 |
---|---|---|
2008 年 7 月 |
トピックを追加 |
情報の拡充 |