WinUI 2+C++/WinRTのXAML IslandsでNarratorのコントロール種別読み上げを抑制する方法|AutomationProperties.LocalizedControlType徹底解説

WinUI 2 + C++/WinRT で XAML Islands を使うと、Narrator が「○○ ― コンボ ボックス」のように技術的な種別名まで読み上げ、利用者の理解を妨げることがあります。本記事では UI Automation の仕組みを整理しながら、AutomationProperties.LocalizedControlType を用いて「必要な情報だけ」を読み上げる実践的な設計・実装・検証手順を詳解します。

目次

問題の正体:なぜ Narrator は「種別」まで読み上げるのか

Windows のスクリーンリーダー(Narrator など)は UI Automation (UIA) のプロパティを参照し、概ね「Name + LocalizedControlType + 状態」の順で読み上げを合成します。たとえば WinUI の ComboBox は既定で コンボ ボックスという種別名 (LocalizedControlType) を持つため、AutomationProperties.Name に「Options Dropdown」を設定していても、結果として「Options Dropdown — コンボ ボックス」と読み上げられます。

これは UIA の設計上、ControlType(論理的な役割)に紐づくローカライズ済みの種別名が自動的に付与されるためです。つまり、読み上げから種別を取り除きたければ、UIA が参照する種別名そのものを置き換える必要があります。

解決の要点:AutomationProperties.LocalizedControlType を上書きする

UIA の読み上げにおける種別文字列は、AutomationProperties.LocalizedControlType で上書きできます。既定の「コンボ ボックス」「ボタン」などの語を、用途が直感的な表現(例:「オプション選択メニュー」)に差し替えることで、Narrator は「Options Dropdown — オプション選択メニュー」のように、技術的な種別語を出さずに、ユーザーに伝えたい言葉に置き換えて読みます。さらに HelpText に短い補足を入れておくと、必要なときにだけ説明を深掘りできます。

最小実装例(C++/WinRT)

// 必要なヘッダー
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Automation.h>
using namespace winrt;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Automation;

// コンボボックス生成
ComboBox myComboBox;
myComboBox.PlaceholderText(L"項目を選択");

// アイテム追加
myComboBox.Items().Append(winrt::box_value(L"選択肢 1"));
myComboBox.Items().Append(winrt::box_value(L"選択肢 2"));

// アクセシビリティ設定
AutomationProperties::SetName(myComboBox, L"Options Dropdown");
AutomationProperties::SetLocalizedControlType(myComboBox, L"オプション選択メニュー"); // 種別読み上げを上書き
AutomationProperties::SetHelpText(myComboBox, L"一覧から 1 つ選んでください。");
AutomationProperties::SetIsRequiredForForm(myComboBox, true); 

読み上げ文を「役立つ日本語」に整える設計指針

  • 完全に黙らせない: 種別語を消すのではなく、用途が直感的な短い日本語に置き換える(例:「ドロップダウン」ではなく「オプション選択メニュー」)。
  • 冗長さを避ける: Name と LocalizedControlType で意味が重複しないようにする(悪例:「設定メニュー — 設定メニュー」)。
  • 補助説明は HelpText に: 操作方法・入力規則・必須性は HelpText と IsRequiredForForm に集約する。
  • 一貫性: 画面内の同種コントロールで表現を統一する。
  • 多言語前提: 将来のローカライズを見越し、文字列はリソース化する。

よく使うコントロールの推奨表現例

UI コントロール既定の種別(例)推奨 LocalizedControlType(例)補足(HelpText 例)
ComboBoxコンボ ボックスオプション選択メニュー一覧から 1 つ選んでください
Buttonボタン実行押すと設定を保存します
ToggleSwitchトグルオン/オフ切り替えオンにすると通知を受け取ります
CheckBoxチェック ボックス選択項目チェックするとメール配信に同意します
TextBox編集入力欄半角英数字で 8~20 文字

WinUI 2 + XAML Islands(C++/WinRT)での実装ポイント

1) 名前空間と参照

UIA 連携には Windows.UI.Xaml.Automation 名前空間が必要です。ビルド環境に WinUI 2 と C++/WinRT を導入済みであれば、上記ヘッダーの #include で利用できます。

2) 生成直後に設定する

AutomationProperties は XAML 要素にアタッチされるため、コントロール生成後すぐに設定します。テンプレート適用後に上書きしても構いませんが、後勝ちである点に注意してください。

3) Name は残す

Name は検索やショートカット、ラベル関連付け(LabeledBy)にも使われます。LocalizedControlType を置き換えても、わかりやすい Name を必ず設定しましょう。

4) HelpText と Required の使い分け

  • HelpText:操作ヒント・入力ルール・エラーメッセージの要約。
  • IsRequiredForForm:必須入力の明示。Narrator が必要時に読み上げます。

5) 他コントロールへの適用例

Button saveButton;
saveButton.Content(box_value(L"保存"));
AutomationProperties::SetName(saveButton, L"保存");
AutomationProperties::SetLocalizedControlType(saveButton, L"実行");
AutomationProperties::SetHelpText(saveButton, L"押すと設定を保存します");

ToggleSwitch notifySwitch;
notifySwitch.OffContent(box_value(L"通知オフ"));
notifySwitch.OnContent(box_value(L"通知オン"));
AutomationProperties::SetName(notifySwitch, L"通知");
AutomationProperties::SetLocalizedControlType(notifySwitch, L"オン/オフ切り替え");
AutomationProperties::SetHelpText(notifySwitch, L"オンにすると新着をお知らせします"); 

UI Automation の基礎整理(実務に効く最小限)

  • ControlType:論理役割(Button, ComboBox など)。
  • LocalizedControlType:上記のローカライズ済み表示名。これを上書きするのが本記事の要点。
  • Name:その要素の識別名。基本的にラベル相当。
  • HelpText:補足説明。
  • LabeledBy:別要素(ラベル)との関連付け。Name を補完。
  • AutomationPeer:UIA へプロパティやパターンを公開する仲介クラス。

既定の XAML コントロールは内部の AutomationPeer が UIA プロパティを提供します。LocalizedControlType をアタッチで上書きすると、既定の読み上げ文を望む表現に置換できます。

カスタムコントロールの場合:AutomationPeer をオーバーライド

独自コントロールや独自テンプレートを持つ場合は、対応する AutomationPeer で GetLocalizedControlTypeCore() をオーバーライドします。以下は概念例です。

struct MyComboBox : winrt::Windows::UI::Xaml::Controls::ComboBox
{
  using base_type = winrt::Windows::UI::Xaml::Controls::ComboBox;
  Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer() override
  {
    return winrt::make<MyComboBoxAutomationPeer>(*this);
  }
};

struct MyComboBoxAutomationPeer
: winrt::Windows::UI::Xaml::Automation::Peers::ComboBoxAutomationPeerT
{
using base_type = winrt::Windows::UI::Xaml::Automation::Peers::ComboBoxAutomationPeerT;

MyComboBoxAutomationPeer(MyComboBox const& owner) : base_type(owner) {}

hstring GetLocalizedControlTypeCore() const override
{
return L"オプション選択メニュー"; // 読み上げ種別を置き換える
}

// 必要に応じて他の Core メソッドも調整
}; 

可能であればまずは アタッチドプロパティの上書き(= XAML/既定コントロールに対する AutomationProperties::SetLocalizedControlType)から着手し、本当に必要になった場合のみ AutomationPeer の実装へ進むのが保守容易性の面で推奨です。

XAML Islands 特有の留意点(デスクトップホスト)

  • ホスト階層: Win32 親ウィンドウに XAML ツリーが埋め込まれます。UIA ツリーでもホスト境界が見えるため、ホスト側に Automation の干渉をしないのが基本です。
  • ライフサイクル: Island の生成直後に AutomationProperties を設定する。テンプレート再適用時の再設定も検討する。
  • 文字入力言語: 日本語環境では Language(x:Uid の文化情報)とリソースの整合性に注意。異なる言語混在は読み上げ品質を下げます。
  • フォーカス移動: ホスト側のフォーカス手当(TabOrder)を正す。読み上げ順と一致させる。

検証手順:Narrator と開発者ツールで確認する

  1. Narrator を起動(ショートカット:Win + Ctrl + Enter)。
  2. 対象コントロールへフォーカスを移動し、読み上げ文を確認。
  3. 想定どおりでなければ Name・LocalizedControlType・HelpText を見直す。
  4. 開発者向けツール(Inspect など)で UIA プロパティを確認:LocalizedControlType が意図した語になっているか、Name が適切かをチェック。
  5. 状態(必須/エラー/有効無効)も併せて検証する。

検証観点チェックリスト

観点合格条件備考
読み上げの簡潔さ10~15 文字程度で要点が伝わる冗長な重複を避ける
一貫性同種コントロールで表現が統一画面/アプリ全体で整える
誤解のなさ技術用語や略語に依存しないユーザーの語彙を前提にしない
補助説明HelpText で補足が得られる必須・入力規則・操作の説明
多言語対応文字列がリソース化済み日本語以外でも適用可能

アンチパターン:やってしまいがちな誤り

  • Name にすべて押し込む: 「Options Dropdown(コンボ ボックス)」のように Name に種別を含めると冗長化。UIA の合成規則に任せるか、LocalizedControlType を適切に上書きする。
  • LocalizedControlType を空にする: 読み上げが不安定になったり、支援技術間の互換性を損ねることがあります。意味のある短い語に置き換えるのが安全。
  • HelpText に操作説明を過剰記述: 長文はユーザー負荷。要点だけ記し、詳細は別 UI(ヘルプ、ツールチップ等)に逃がす。
  • 画面内で表現がばらつく: 「メニュー」「リスト」「一覧」など語の揺れは理解を阻害。一貫性を重視。

具体的な改善の流れ(サンプル)

  1. 要素を洗い出し、既定の読み上げ文(Name + 種別 + 状態)を採取。
  2. 重複・冗長箇所を特定し、LocalizedControlType の候補語を設計。
  3. Name・LocalizedControlType・HelpText の 3 点セットを実装。
  4. Narrator で確認し、ユーザーテストで可読性を評価。
  5. パターン化してコンポーネント化(設計ガイド・ヘルパー API を作成)。

ヘルパー関数で運用コストを下げる

template &lt;typename T&gt;
void ConfigureAccessibleControl(
    T const&amp; element,
    hstring const&amp; name,
    hstring const&amp; localizedType,
    hstring const&amp; help,
    bool required = false)
{
  AutomationProperties::SetName(element, name);
  AutomationProperties::SetLocalizedControlType(element, localizedType);
  if (!help.empty()) AutomationProperties::SetHelpText(element, help);
  AutomationProperties::SetIsRequiredForForm(element, required);
}

この関数をプロジェクト共通ユーティリティとして用意すれば、可読性の高い読み上げをチーム全体で一貫して適用できます。

アクセシビリティとプロダクト価値の両立

一部のユーザーは「ボタン」「チェック ボックス」といった役割語を好みます。LocalizedControlType を置換する際は、用途が伝わる代替語(例:「実行」「選択項目」「オン/オフ切り替え」)を選び、役割のニュアンスを損なわないように注意しましょう。読み上げを単に短くするのではなく、判断と操作に必要な文脈を残すのがコツです。

ケーススタディ:フォームの項目選択を最短で理解させる

プロフィール編集画面の「職種」選択(ComboBox)で、既定の読み上げは「職種 — コンボ ボックス — 折りたたみ」。ユーザーは「何を選ぶのか」は理解できても、技術用語が混ざるため理解の瞬発力が落ちます。以下のように調整すると、意味は変えずに情報密度を上げられます。

AutomationProperties::SetName(jobCombo, L"職種");
AutomationProperties::SetLocalizedControlType(jobCombo, L"オプション選択メニュー");
AutomationProperties::SetHelpText(jobCombo, L"一覧から該当する職種を 1 つ選んでください");

期待される読み上げ:「職種 — オプション選択メニュー」。必要十分で、かつ即時に理解できます。

多言語対応のベストプラクティス

  • 文字列はすべてリソース化し、LocalizedControlType も言語ごとに提供する。
  • 日英で語の長さが異なる点を踏まえ、過度に長い語を避ける。
  • 言語切替時は必ず Narrator で読み上げ確認を行う。

既存画面の棚卸しに使える「短縮方針」サンプル

現状(例)改善後(例)狙い
「設定 — ボタン」「設定 — 実行」役割語を用途語へ置換
「通知 — トグル — オフ」「通知 — オン/オフ切り替え — オフ」「トグル」を避け直感化
「職種 — コンボ ボックス」「職種 — オプション選択メニュー」技術語の除去
「メール — 編集」「メール — 入力欄」意味の明確化

トラブルシュート

  • 上書きが効かない: テンプレート再適用や再生成で値が戻る場合があります。ロード完了後の再設定やバインディングでの適用を検討。
  • 読み上げが二重化: Name と LocalizedControlType が同じ語になっていないか確認。
  • 一部画面だけ既定語が混入: 共有コンポーネント側で既定値が設定されている可能性。コンポーネントの初期化順を見直す。
  • 状態の読み上げ不足: 必須やエラーの状態は IsRequiredForForm や検証エラーの可視化とセットで。

まとめ

WinUI 2 + C++/WinRT の XAML Islands で Narrator に余計な「コントロール種別」を読ませたくない場合は、AutomationProperties.LocalizedControlType を戦略的に上書きするのがシンプルで確実です。Name と HelpText を組み合わせ、短く・一貫し・誤解のない言葉で UI を説明できれば、キーボード/読み上げ環境の双方で操作効率が向上します。プロダクト全体へパターンとして展開し、検証ループを回していきましょう。


サンプル完全版(抜粋:フォーム片段)

Grid root;

// 職種(コンボボックス)
ComboBox jobCombo;
jobCombo.PlaceholderText(L"選択してください");
jobCombo.Items().Append(box_value(L"エンジニア"));
jobCombo.Items().Append(box_value(L"デザイナー"));
jobCombo.Items().Append(box_value(L"プロダクトマネージャー"));
AutomationProperties::SetName(jobCombo, L"職種");
AutomationProperties::SetLocalizedControlType(jobCombo, L"オプション選択メニュー");
AutomationProperties::SetHelpText(jobCombo, L"一覧から 1 つ選んでください");
AutomationProperties::SetIsRequiredForForm(jobCombo, true);

// 保存(ボタン)
Button saveBtn;
saveBtn.Content(box_value(L"保存"));
AutomationProperties::SetName(saveBtn, L"保存");
AutomationProperties::SetLocalizedControlType(saveBtn, L"実行");
AutomationProperties::SetHelpText(saveBtn, L"押すと入力内容を保存します");

// 通知(トグル)
ToggleSwitch notifySwitch;
notifySwitch.OffContent(box_value(L"通知オフ"));
notifySwitch.OnContent(box_value(L"通知オン"));
AutomationProperties::SetName(notifySwitch, L"通知");
AutomationProperties::SetLocalizedControlType(notifySwitch, L"オン/オフ切り替え");
AutomationProperties::SetHelpText(notifySwitch, L"オンにすると新着をお知らせします");

// ルートへ追加(レイアウトコードは実際の UI に合わせて調整)
root.Children().Append(jobCombo);
root.Children().Append(saveBtn);
root.Children().Append(notifySwitch); 

実装チェック用クイックリファレンス

  • やること: SetName / SetLocalizedControlType / SetHelpText / SetIsRequiredForForm
  • 禁忌: 技術語の残置、重複表現、長文の HelpText、未リソース化
  • 検証: Narrator の実読み上げと UIA プロパティを両面で確認

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次